diff options
author | Vadim Bendebury <vbendeb@chromium.org> | 2011-09-11 21:49:39 -0700 |
---|---|---|
committer | Vadim Bendebury <vbendeb@chromium.org> | 2011-09-12 15:54:38 -0700 |
commit | fc1ad78beffc1043313b0ec08c961d93cbae0ee8 (patch) | |
tree | 521588942c36e55c023ffd2f1783bb3a72fe9510 /drivers/spi | |
parent | e82ef9a5caec018af0a9bd2aea9870358b1f1313 (diff) |
Fix ICH SPI driver to work for write transactions.
There have been a couple of problems with the driver:
- the commands which do not involve data transfer (WREN most
notably) were not being issued.
- the write page command was sent trying to send address as data,
which was causing the 3 bytes of the command to overflow to the
next cycle
This change fixes these problems and also refactors the driver
such that the status polling with timeout is implemented once and
used from different places.
BUG=chromium-os:20105
TEST=manual
. this change was tested combined with other modifications; The
ability to read and save the u-boot environment in an SPI
flash was demonstrated.
Change-Id: I12da0363eaf9ae193d1d29a84a9a2b48cbd58fd8
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: http://gerrit.chromium.org/gerrit/7526
Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Gabe Black <gabeblack@chromium.org>
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/ich.c | 132 |
1 files changed, 82 insertions, 50 deletions
diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index 93871467f16..166a85fe028 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -421,17 +421,27 @@ static void spi_setup_type(spi_transaction *trans) trans->type = 0xFF; /* Try to guess spi type from read/write sizes. */ - if (trans->bytesin == 0) - /* If bytesin = 0 and bytesout >= 4, we don't know if - * it's WRITE_NO_ADDRESS or WRITE_WITH_ADDRESS. If we - * use WRITE_NO_ADDRESS and the first 3 data bytes are - * actual the address, they go to the bus anyhow. - */ - trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS; - else if (trans->bytesout == 1) /* and bytesin is > 0 */ + if (trans->bytesin == 0) { + if (trans->bytesout > 4) + /* + * If bytesin = 0 and bytesout > 4, we presume this is + * a write data operation, which is accompanied by an + * address. + */ + trans->type = SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS; + else + trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS; + return; + } + + if (trans->bytesout == 1) { /* and bytesin is > 0 */ trans->type = SPI_OPCODE_TYPE_READ_NO_ADDRESS; - else if (trans->bytesout == 4) /* and bytesin is > 0 */ + return; + } + + if (trans->bytesout == 4) { /* and bytesin is > 0 */ trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS; + } } static int spi_setup_opcode(spi_transaction *trans) @@ -503,14 +513,40 @@ static int spi_setup_offset(spi_transaction *trans) } } +/* + * Wait for up to 60ms til status register bit(s) turn 1 (in case wait_til_set + * below is True) or 0. In case the wait was for the bit(s) to set - write + * those bits back, which would cause resetting them. + * + * Return the last read status value on success or -1 on failure. + */ +static int ich_status_poll(u16 bitmask, int wait_til_set) +{ + int timeout = 6000; /* This will result in 60 ms */ + u16 status = 0; + + while (timeout--) { + status = readw_(cntlr.status); + if (wait_til_set ^ ((status & bitmask) == 0)) { + if (wait_til_set) + writew_((status & bitmask), cntlr.status); + return status; + } + udelay(10); + } + + printf("ICH SPI: SCIP timeout, read %x, expected %x\n", + status, bitmask); + return -1; +} + int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bitsout, void *din, unsigned int bitsin) { - int timeout; - - uint16_t status, control; + uint16_t control; int16_t opcode_index; int with_address; + int status; spi_transaction trans = { dout, bitsout / 8, @@ -535,13 +571,10 @@ int spi_xfer(struct spi_slave *slave, const void *dout, } /* 60 ms are 9.6 million cycles at 16 MHz. */ - timeout = 100 * 60; - while ((readb_(cntlr.status) & SPIS_SCIP) && --timeout) - udelay(10); - if (!timeout) { - puts("ICH SPI: SCIP never cleared\n"); + if (ich_status_poll(SPIS_SCIP, 0) == -1) return 1; - } + + writew_(SPIS_CDS | SPIS_FCERR, cntlr.status); spi_setup_type(&trans); if ((opcode_index = spi_setup_opcode(&trans)) < 0) @@ -549,6 +582,31 @@ int spi_xfer(struct spi_slave *slave, const void *dout, if ((with_address = spi_setup_offset(&trans)) < 0) return 1; + /* Preset control fields */ + control = SPIC_SCGO | ((opcode_index & 0x07) << 4); + + if (!trans.bytesout && !trans.bytesin) { + /* + * This is a 'no data' command (like Write Enable), its + * bitesout size was 1, decremented to zero while executing + * spi_setup_opcode() above. Tell the chip to send the + * command. + */ + writew_(control, cntlr.control); + + /* wait for the result */ + status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1); + if (status == -1) + return 1; + + if (status & SPIS_FCERR) { + puts("ICH SPI: Transaction error\n"); + return 1; + } + + return 0; + } + /* * Read or write up to databytes bytes at a time until everything has * been sent. @@ -572,47 +630,21 @@ int spi_xfer(struct spi_slave *slave, const void *dout, trans.offset += data_length; } - /* Assemble the status register */ - status = readb_(cntlr.status); - /* keep reserved bits */ - status &= SPIS_RESERVED_MASK; - /* clear error status registers */ - status |= (SPIS_CDS | SPIS_FCERR); - writeb_(status, cntlr.status); - - /* Assemble the control register */ - control = (opcode_index & 0x07) << 4; - - if (data_length != 0) { - control |= SPIC_DS; - control |= (data_length - 1) << 8; - } - - timeout = 100 * 60; - - /* Start */ - control |= SPIC_SCGO; + /* Add proper control fields' values */ + control &= ~((cntlr.databytes - 1) << 8); + control |= SPIC_DS; + control |= (data_length - 1) << 8; /* write it */ writew_(control, cntlr.control); /* Wait for Cycle Done Status or Flash Cycle Error. */ - status = readb_(cntlr.status); - while (((status & (SPIS_CDS | SPIS_FCERR)) == 0) && --timeout) { - udelay(10); - status = readb_(cntlr.status); - } - if (!timeout) { - printf("timeout, status = 0x%04x\n", - readb_(cntlr.status)); + status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1); + if (status == -1) return 1; - } if (status & SPIS_FCERR) { puts("ICH SPI: Transaction error\n"); - /* keep reserved bits */ - status &= SPIS_RESERVED_MASK; - writeb_(status | SPIS_FCERR, cntlr.status); return 1; } |