summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDuncan Laurie <dlaurie@chromium.org>2011-11-14 10:31:36 -0800
committerDuncan Laurie <dlaurie@chromium.org>2011-11-14 11:16:13 -0800
commit9b85a281c8a17d7c9e10ec2c899a26dd1c907072 (patch)
treea24e3d1d11426d5f3fd3fb6e17ec30ebcb3c2b3f /drivers
parent21f54150d1500cff386f9429735f274d0a066943 (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.c21
1 files changed, 21 insertions, 0 deletions
diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c
index fa05a9a6ea..7a168e9310 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;
}