mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-10 15:19:51 +00:00
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/4660 Reported-by: Aleksandar Ivanov <ivanov.aleks@gmail.com> Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> Tested-by: Aleksandar Ivanov <ivanov.aleks@gmail.com> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Jean Delvare <khali@linux-fr.org>
This commit is contained in:
parent
bbd2d9c919
commit
4d29196c53
@ -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,22 +184,17 @@ 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",
|
|
||||||
count, offset, status);
|
|
||||||
return (status < 0) ? -EIO : status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When we have a better choice than SMBus calls, use a combined
|
* When we have a better choice than SMBus calls, use a
|
||||||
* I2C message. Write address; then read up to io_limit data bytes.
|
* combined I2C message. Write address; then read up to
|
||||||
* Note that read page rollover helps us here (unlike writes).
|
* io_limit data bytes. Note that read page rollover helps us
|
||||||
* msgbuf is u8 and will cast to our needs.
|
* here (unlike writes). msgbuf is u8 and will cast to our
|
||||||
|
* needs.
|
||||||
*/
|
*/
|
||||||
i = 0;
|
i = 0;
|
||||||
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
||||||
@ -213,17 +209,35 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
|
|||||||
msg[1].flags = I2C_M_RD;
|
msg[1].flags = I2C_M_RD;
|
||||||
msg[1].buf = buf;
|
msg[1].buf = buf;
|
||||||
msg[1].len = count;
|
msg[1].len = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads fail if the previous write didn't complete yet. We may
|
||||||
|
* loop a few times until this one succeeds, waiting at least
|
||||||
|
* long enough for one entire page write to work.
|
||||||
|
*/
|
||||||
|
timeout = jiffies + msecs_to_jiffies(write_timeout);
|
||||||
|
do {
|
||||||
|
read_time = jiffies;
|
||||||
|
if (at24->use_smbus) {
|
||||||
|
status = i2c_smbus_read_i2c_block_data(client, offset,
|
||||||
|
count, buf);
|
||||||
|
} else {
|
||||||
status = i2c_transfer(client->adapter, msg, 2);
|
status = i2c_transfer(client->adapter, msg, 2);
|
||||||
dev_dbg(&client->dev, "i2c read %zu@%d --> %d\n",
|
|
||||||
count, offset, status);
|
|
||||||
|
|
||||||
if (status == 2)
|
if (status == 2)
|
||||||
|
status = count;
|
||||||
|
}
|
||||||
|
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
|
||||||
|
count, offset, status, jiffies);
|
||||||
|
|
||||||
|
if (status == count)
|
||||||
return count;
|
return count;
|
||||||
else if (status >= 0)
|
|
||||||
return -EIO;
|
/* REVISIT: at HZ=100, this is sloooow */
|
||||||
else
|
msleep(1);
|
||||||
return status;
|
} while (time_before(read_time, timeout));
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t at24_read(struct at24_data *at24,
|
static ssize_t at24_read(struct at24_data *at24,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user