mtd: sst25l: fix multi-part messages with broken spi masters
Some SPI masters (ep93xx) have limitations when using the SFRMOUT signal for the spi device chip select. The SFRMOUT signal is only asserted as long as the spi transmit fifo contains data. As soon as the last bit is clocked into the receive fifo it gets deasserted. The functions sst25l_status and sst25l_match_device use the API function spi_write_then_read to write a command to the flash then read the response back. This API function creates a two part spi message for the write then read. When this message is transferred the SFRMOUT signal ends up getting deasserted after the command phase. This causes the command to get aborted by the device so the read phase returns invalid data. By changing sst25l_status and sst25l_match_device to use a single transfer synchronous message, the SFRMOUT signal stays asserted during the entire message so the correct data always gets returned. This change will have no effect on SPI masters which use a chip select mechanism (GPIO's, etc.) which does stay asserted correctly. As a bonus, the single transfer synchronous messages complete faster than multi-part messages. Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
parent
46f3e88bd9
commit
0ffe0ce36e
1 changed files with 33 additions and 24 deletions
|
@ -73,15 +73,25 @@ static struct flash_info __initdata sst25l_flash_info[] = {
|
|||
|
||||
static int sst25l_status(struct sst25l_flash *flash, int *status)
|
||||
{
|
||||
unsigned char command, response;
|
||||
struct spi_message m;
|
||||
struct spi_transfer t;
|
||||
unsigned char cmd_resp[2];
|
||||
int err;
|
||||
|
||||
command = SST25L_CMD_RDSR;
|
||||
err = spi_write_then_read(flash->spi, &command, 1, &response, 1);
|
||||
spi_message_init(&m);
|
||||
memset(&t, 0, sizeof(struct spi_transfer));
|
||||
|
||||
cmd_resp[0] = SST25L_CMD_RDSR;
|
||||
cmd_resp[1] = 0xff;
|
||||
t.tx_buf = cmd_resp;
|
||||
t.rx_buf = cmd_resp;
|
||||
t.len = sizeof(cmd_resp);
|
||||
spi_message_add_tail(&t, &m);
|
||||
err = spi_sync(flash->spi, &m);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*status = response;
|
||||
*status = cmd_resp[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -328,33 +338,32 @@ static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
static struct flash_info *__init sst25l_match_device(struct spi_device *spi)
|
||||
{
|
||||
struct flash_info *flash_info = NULL;
|
||||
unsigned char command[4], response;
|
||||
struct spi_message m;
|
||||
struct spi_transfer t;
|
||||
unsigned char cmd_resp[6];
|
||||
int i, err;
|
||||
uint16_t id;
|
||||
|
||||
command[0] = SST25L_CMD_READ_ID;
|
||||
command[1] = 0;
|
||||
command[2] = 0;
|
||||
command[3] = 0;
|
||||
err = spi_write_then_read(spi, command, sizeof(command), &response, 1);
|
||||
spi_message_init(&m);
|
||||
memset(&t, 0, sizeof(struct spi_transfer));
|
||||
|
||||
cmd_resp[0] = SST25L_CMD_READ_ID;
|
||||
cmd_resp[1] = 0;
|
||||
cmd_resp[2] = 0;
|
||||
cmd_resp[3] = 0;
|
||||
cmd_resp[4] = 0xff;
|
||||
cmd_resp[5] = 0xff;
|
||||
t.tx_buf = cmd_resp;
|
||||
t.rx_buf = cmd_resp;
|
||||
t.len = sizeof(cmd_resp);
|
||||
spi_message_add_tail(&t, &m);
|
||||
err = spi_sync(spi, &m);
|
||||
if (err < 0) {
|
||||
dev_err(&spi->dev, "error reading device id msb\n");
|
||||
dev_err(&spi->dev, "error reading device id\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id = response << 8;
|
||||
|
||||
command[0] = SST25L_CMD_READ_ID;
|
||||
command[1] = 0;
|
||||
command[2] = 0;
|
||||
command[3] = 1;
|
||||
err = spi_write_then_read(spi, command, sizeof(command), &response, 1);
|
||||
if (err < 0) {
|
||||
dev_err(&spi->dev, "error reading device id lsb\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id |= response;
|
||||
id = (cmd_resp[4] << 8) | cmd_resp[5];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++)
|
||||
if (sst25l_flash_info[i].device_id == id)
|
||||
|
|
Loading…
Reference in a new issue