Commit 4d29196c authored by Wolfram Sang's avatar Wolfram Sang Committed by Jean Delvare

at24: Use timeout also for read

Writes may take some time on EEPROMs, so for consecutive writes, we already
have a loop waiting for the EEPROM to become ready. Use such a loop for reads,
too, in case somebody wants to immediately read after a write. Detailed bug
report and test case can be found here:

http://article.gmane.org/gmane.linux.drivers.i2c/4660Reported-by: default avatarAleksandar Ivanov <ivanov.aleks@gmail.com>
Signed-off-by: default avatarWolfram Sang <w.sang@pengutronix.de>
Tested-by: default avatarAleksandar Ivanov <ivanov.aleks@gmail.com>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent bbd2d9c9
...@@ -158,6 +158,7 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, ...@@ -158,6 +158,7 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
struct i2c_msg msg[2]; struct i2c_msg msg[2];
u8 msgbuf[2]; u8 msgbuf[2];
struct i2c_client *client; struct i2c_client *client;
unsigned long timeout, read_time;
int status, i; int status, i;
memset(msg, 0, sizeof(msg)); memset(msg, 0, sizeof(msg));
...@@ -183,47 +184,60 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, ...@@ -183,47 +184,60 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
if (count > io_limit) if (count > io_limit)
count = io_limit; count = io_limit;
/* Smaller eeproms can work given some SMBus extension calls */
if (at24->use_smbus) { if (at24->use_smbus) {
/* Smaller eeproms can work given some SMBus extension calls */
if (count > I2C_SMBUS_BLOCK_MAX) if (count > I2C_SMBUS_BLOCK_MAX)
count = I2C_SMBUS_BLOCK_MAX; count = I2C_SMBUS_BLOCK_MAX;
status = i2c_smbus_read_i2c_block_data(client, offset, } else {
count, buf); /*
dev_dbg(&client->dev, "smbus read %zu@%d --> %d\n", * When we have a better choice than SMBus calls, use a
count, offset, status); * combined I2C message. Write address; then read up to
return (status < 0) ? -EIO : status; * io_limit data bytes. Note that read page rollover helps us
* here (unlike writes). msgbuf is u8 and will cast to our
* needs.
*/
i = 0;
if (at24->chip.flags & AT24_FLAG_ADDR16)
msgbuf[i++] = offset >> 8;
msgbuf[i++] = offset;
msg[0].addr = client->addr;
msg[0].buf = msgbuf;
msg[0].len = i;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
msg[1].len = count;
} }
/* /*
* When we have a better choice than SMBus calls, use a combined * Reads fail if the previous write didn't complete yet. We may
* I2C message. Write address; then read up to io_limit data bytes. * loop a few times until this one succeeds, waiting at least
* Note that read page rollover helps us here (unlike writes). * long enough for one entire page write to work.
* msgbuf is u8 and will cast to our needs.
*/ */
i = 0; timeout = jiffies + msecs_to_jiffies(write_timeout);
if (at24->chip.flags & AT24_FLAG_ADDR16) do {
msgbuf[i++] = offset >> 8; read_time = jiffies;
msgbuf[i++] = offset; if (at24->use_smbus) {
status = i2c_smbus_read_i2c_block_data(client, offset,
msg[0].addr = client->addr; count, buf);
msg[0].buf = msgbuf; } else {
msg[0].len = i; status = i2c_transfer(client->adapter, msg, 2);
if (status == 2)
status = count;
}
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
count, offset, status, jiffies);
msg[1].addr = client->addr; if (status == count)
msg[1].flags = I2C_M_RD; return count;
msg[1].buf = buf;
msg[1].len = count;
status = i2c_transfer(client->adapter, msg, 2); /* REVISIT: at HZ=100, this is sloooow */
dev_dbg(&client->dev, "i2c read %zu@%d --> %d\n", msleep(1);
count, offset, status); } while (time_before(read_time, timeout));
if (status == 2) return -ETIMEDOUT;
return count;
else if (status >= 0)
return -EIO;
else
return status;
} }
static ssize_t at24_read(struct at24_data *at24, static ssize_t at24_read(struct at24_data *at24,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment