diff options
author | Wolfram Sang <w.sang@pengutronix.de> | 2009-11-26 03:22:33 -0500 |
---|---|---|
committer | Jean Delvare <khali@linux-fr.org> | 2009-11-26 03:22:33 -0500 |
commit | 4d29196c535088e807061ce2a0aa526daec2edfb (patch) | |
tree | 960fbfa00bb0372a6be425b8131dd4d937e5d0d8 /drivers/misc | |
parent | bbd2d9c9198c6efd449e9d395b3eaf2d03aa3bba (diff) |
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>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/eeprom/at24.c | 76 |
1 files changed, 45 insertions, 31 deletions
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index db39f4a52f53..2cb2736d65aa 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c | |||
@@ -158,6 +158,7 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, | |||
158 | struct i2c_msg msg[2]; | 158 | struct i2c_msg msg[2]; |
159 | u8 msgbuf[2]; | 159 | u8 msgbuf[2]; |
160 | struct i2c_client *client; | 160 | struct i2c_client *client; |
161 | unsigned long timeout, read_time; | ||
161 | int status, i; | 162 | int status, i; |
162 | 163 | ||
163 | memset(msg, 0, sizeof(msg)); | 164 | memset(msg, 0, sizeof(msg)); |
@@ -183,47 +184,60 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, | |||
183 | if (count > io_limit) | 184 | if (count > io_limit) |
184 | count = io_limit; | 185 | count = io_limit; |
185 | 186 | ||
186 | /* Smaller eeproms can work given some SMBus extension calls */ | ||
187 | if (at24->use_smbus) { | 187 | if (at24->use_smbus) { |
188 | /* Smaller eeproms can work given some SMBus extension calls */ | ||
188 | if (count > I2C_SMBUS_BLOCK_MAX) | 189 | if (count > I2C_SMBUS_BLOCK_MAX) |
189 | count = I2C_SMBUS_BLOCK_MAX; | 190 | count = I2C_SMBUS_BLOCK_MAX; |
190 | status = i2c_smbus_read_i2c_block_data(client, offset, | 191 | } else { |
191 | count, buf); | 192 | /* |
192 | dev_dbg(&client->dev, "smbus read %zu@%d --> %d\n", | 193 | * When we have a better choice than SMBus calls, use a |
193 | count, offset, status); | 194 | * combined I2C message. Write address; then read up to |
194 | return (status < 0) ? -EIO : status; | 195 | * io_limit data bytes. Note that read page rollover helps us |
196 | * here (unlike writes). msgbuf is u8 and will cast to our | ||
197 | * needs. | ||
198 | */ | ||
199 | i = 0; | ||
200 | if (at24->chip.flags & AT24_FLAG_ADDR16) | ||
201 | msgbuf[i++] = offset >> 8; | ||
202 | msgbuf[i++] = offset; | ||
203 | |||
204 | msg[0].addr = client->addr; | ||
205 | msg[0].buf = msgbuf; | ||
206 | msg[0].len = i; | ||
207 | |||
208 | msg[1].addr = client->addr; | ||
209 | msg[1].flags = I2C_M_RD; | ||
210 | msg[1].buf = buf; | ||
211 | msg[1].len = count; | ||
195 | } | 212 | } |
196 | 213 | ||
197 | /* | 214 | /* |
198 | * When we have a better choice than SMBus calls, use a combined | 215 | * Reads fail if the previous write didn't complete yet. We may |
199 | * I2C message. Write address; then read up to io_limit data bytes. | 216 | * loop a few times until this one succeeds, waiting at least |
200 | * Note that read page rollover helps us here (unlike writes). | 217 | * long enough for one entire page write to work. |
201 | * msgbuf is u8 and will cast to our needs. | ||
202 | */ | 218 | */ |
203 | i = 0; | 219 | timeout = jiffies + msecs_to_jiffies(write_timeout); |
204 | if (at24->chip.flags & AT24_FLAG_ADDR16) | 220 | do { |
205 | msgbuf[i++] = offset >> 8; | 221 | read_time = jiffies; |
206 | msgbuf[i++] = offset; | 222 | if (at24->use_smbus) { |
207 | 223 | status = i2c_smbus_read_i2c_block_data(client, offset, | |
208 | msg[0].addr = client->addr; | 224 | count, buf); |
209 | msg[0].buf = msgbuf; | 225 | } else { |
210 | msg[0].len = i; | 226 | status = i2c_transfer(client->adapter, msg, 2); |
227 | if (status == 2) | ||
228 | status = count; | ||
229 | } | ||
230 | dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", | ||
231 | count, offset, status, jiffies); | ||
211 | 232 | ||
212 | msg[1].addr = client->addr; | 233 | if (status == count) |
213 | msg[1].flags = I2C_M_RD; | 234 | return count; |
214 | msg[1].buf = buf; | ||
215 | msg[1].len = count; | ||
216 | 235 | ||
217 | status = i2c_transfer(client->adapter, msg, 2); | 236 | /* REVISIT: at HZ=100, this is sloooow */ |
218 | dev_dbg(&client->dev, "i2c read %zu@%d --> %d\n", | 237 | msleep(1); |
219 | count, offset, status); | 238 | } while (time_before(read_time, timeout)); |
220 | 239 | ||
221 | if (status == 2) | 240 | return -ETIMEDOUT; |
222 | return count; | ||
223 | else if (status >= 0) | ||
224 | return -EIO; | ||
225 | else | ||
226 | return status; | ||
227 | } | 241 | } |
228 | 242 | ||
229 | static ssize_t at24_read(struct at24_data *at24, | 243 | static ssize_t at24_read(struct at24_data *at24, |