diff options
author | Duncan Laurie <dlaurie@chromium.org> | 2011-11-14 10:31:36 -0800 |
---|---|---|
committer | Duncan Laurie <dlaurie@chromium.org> | 2011-11-14 11:16:13 -0800 |
commit | 9b85a281c8a17d7c9e10ec2c899a26dd1c907072 (patch) | |
tree | a24e3d1d11426d5f3fd3fb6e17ec30ebcb3c2b3f /drivers | |
parent | 21f54150d1500cff386f9429735f274d0a066943 (diff) |
ICH SPI: Use an atomic preop for Write Enable
The U-boot spi interface uses Software Sequencing and handles
write transactions in three distinct steps:
1) issue Write Enable op
2) issue Page Program op
3) poll Read Status Reg for completion
However in an Intel 6-series chipset the Management Engine is
also issuing a lot of transactions through the same controller
to the same chip. It is possible for an ME transaction to
occur between the U-boot issuing WREN and sending the actual
data, resulting in the host WREN being lost and the data not
actually being written to the chip.
This change intercepts WREN opcode and instead applies it as
a prefix operator for the next issued transaction, ensuring
that the two are issued back-to-back to the SPI chip.
Unfortunately this register is not writable when the SPI
contoller is locked down, so it is not always applicable.
BUG=chrome-os-partner:6690
TEST=repeated manual testing on lumpy with boot/suspend/resume
Change-Id: I75e353942fd6148a93be561ff422e37dfc6dc8c4
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/11625
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/spi/ich.c | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index fa05a9a6ea0..7a168e9310c 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -85,6 +85,7 @@ typedef struct ich_spi_controller { uint8_t *opmenu; int menubytes; + uint16_t *preop; uint16_t *optype; uint32_t *addr; uint8_t *data; @@ -343,6 +344,7 @@ void spi_init(void) cntlr.status = (uint8_t *)&ich7_spi->spis; cntlr.control = &ich7_spi->spic; cntlr.bbar = &ich7_spi->bbar; + cntlr.preop = &ich7_spi->preop; break; } case 9: @@ -360,6 +362,7 @@ void spi_init(void) cntlr.status = &ich9_spi->ssfs; cntlr.control = (uint16_t *)ich9_spi->ssfc; cntlr.bbar = &ich9_spi->bbar; + cntlr.preop = &ich9_spi->preop; break; } default: @@ -590,9 +593,23 @@ int spi_xfer(struct spi_slave *slave, const void *dout, if ((with_address = spi_setup_offset(&trans)) < 0) return -1; + if (!ichspi_lock && trans.opcode == 0x06) { + /* + * Treat Write Enable as Atomic Pre-Op if possible + * in order to prevent the Management Engine from + * issuing a transaction between WREN and DATA. + */ + writew_(trans.opcode, cntlr.preop); + return 0; + } + /* Preset control fields */ control = SPIC_SCGO | ((opcode_index & 0x07) << 4); + /* Issue atomic preop cycle if needed */ + if (readw_(cntlr.preop)) + control |= SPIC_ACS; + if (!trans.bytesout && !trans.bytesin) { /* * This is a 'no data' command (like Write Enable), its @@ -676,5 +693,9 @@ int spi_xfer(struct spi_slave *slave, const void *dout, trans.offset += data_length; } } + + /* Clear atomic preop now that xfer is done */ + writew_(0, cntlr.preop); + return 0; } |