sb/intel/common/spi: Prevent transfers across 4KiB boundaries
The ICH SPI controller fails when a single transfer spans a 4KiB boundary. Limit data_length in spi_ctrlr_xfer() to stay within the current 4KiB page when with_address is true, avoiding the hardware limitation at the platform driver level. This fixes SPI read errors observed on SandyBridge, IvyBridge, Haswell, and Broadwell when reading option variables stored in SMMSTORE. When scanning the store to locate a given variable, reads would often cross into the next 4KiB page (eg, reading 60 bytes from 0x313ff0). TEST=build/boot stumpy, link, beltino, jecht boards, verify no SPI read errors in cbmem, CFR options work properly. Change-Id: I73d9c0acdbbb2faf5caff1f73049bff900774156 Signed-off-by: Matt DeVillier <matt.devillier@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/90689 Reviewed-by: Patrick Rudolph <patrick.rudolph@9elements.com> Reviewed-by: Angel Pons <th3fanbus@gmail.com> Reviewed-by: Jérémy Compostella <jeremy.compostella@intel.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
95ad028274
commit
8bc1372f72
1 changed files with 16 additions and 2 deletions
|
|
@ -619,14 +619,28 @@ static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
|
|||
*/
|
||||
while (trans.bytesout || trans.bytesin) {
|
||||
uint32_t data_length;
|
||||
uint32_t max_length;
|
||||
|
||||
/* SPI addresses are 24 bit only */
|
||||
writel_(trans.offset & 0x00FFFFFF, cntlr.addr);
|
||||
|
||||
if (trans.bytesout)
|
||||
data_length = MIN(trans.bytesout, cntlr.databytes);
|
||||
max_length = MIN(trans.bytesout, cntlr.databytes);
|
||||
else
|
||||
data_length = MIN(trans.bytesin, cntlr.databytes);
|
||||
max_length = MIN(trans.bytesin, cntlr.databytes);
|
||||
|
||||
/*
|
||||
* Avoid transfers that cross 4KiB boundaries. The ICH SPI controller
|
||||
* has been observed to fail on some platforms when a single transfer
|
||||
* spans a 4KiB boundary (e.g., offset 0x...3ff0, len 0x3c crosses 0x...4000).
|
||||
*/
|
||||
if (with_address) {
|
||||
uint32_t off_in_4k = trans.offset & 0xfff;
|
||||
uint32_t max_to_boundary = 0x1000 - off_in_4k;
|
||||
data_length = MIN(max_length, max_to_boundary);
|
||||
} else {
|
||||
data_length = max_length;
|
||||
}
|
||||
|
||||
/* Program data into FDATA0 to N */
|
||||
if (trans.bytesout) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue