drivers: eeprom_at2x: guarantee retry until timeout
Make sure to retry at least once after the timeout elapses. Sample the current time before starting the i2c transaction, and only give up if the polling occurred after the timeout. The timeout exists to allow the eeprom time to complete a write, during which time it will nack transactions (at24) or the status register will report busy (at25). If a transaction fails legitimately, but the 1ms sleep overshoots the timeout expiration, we will not try again, which fails to give the part the full grace period before declaring failure. This is likely to happen in the last 1ms interval but also possible if the eeprom thread is preempted. It is possible to only try once and give up if the sleep lasts longer than the timeout, which fails to give the part an adequate period to complete the write. Waiting until the current time is after (not equal to) the timeout is also important because we don't want to round up partial milliseconds if the start time was sampled near the end of a millisecond boundary. The timeouts of eeproms can be ~5ms. Signed-off-by: Tyler Hall <tylerwhall@gmail.com>
This commit is contained in:
parent
41279af8a4
commit
62a13fb03b
1 changed files with 14 additions and 9 deletions
|
@ -218,15 +218,16 @@ static int eeprom_at24_read(struct device *dev, off_t offset, void *buf,
|
|||
* until the current write cycle should be completed.
|
||||
*/
|
||||
timeout = k_uptime_get() + config->timeout;
|
||||
do {
|
||||
while (1) {
|
||||
int64_t now = k_uptime_get();
|
||||
err = i2c_write_read(data->bus_dev, config->bus_addr,
|
||||
addr, config->addr_width / 8,
|
||||
buf, len);
|
||||
if (!err) {
|
||||
if (!err || now > timeout) {
|
||||
break;
|
||||
}
|
||||
k_sleep(K_MSEC(1));
|
||||
} while (timeout > k_uptime_get());
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -259,15 +260,15 @@ static int eeprom_at24_write(struct device *dev, off_t offset,
|
|||
* completed.
|
||||
*/
|
||||
timeout = k_uptime_get() + config->timeout;
|
||||
do {
|
||||
while (1) {
|
||||
int64_t now = k_uptime_get();
|
||||
err = i2c_write(data->bus_dev, block, sizeof(block),
|
||||
config->bus_addr);
|
||||
if (!err) {
|
||||
if (!err || now > timeout) {
|
||||
break;
|
||||
}
|
||||
|
||||
k_sleep(K_MSEC(1));
|
||||
} while (timeout > k_uptime_get());
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
return err;
|
||||
|
@ -317,7 +318,8 @@ static int eeprom_at25_wait_for_idle(struct device *dev)
|
|||
int err;
|
||||
|
||||
timeout = k_uptime_get() + config->timeout;
|
||||
do {
|
||||
while (1) {
|
||||
int64_t now = k_uptime_get();
|
||||
err = eeprom_at25_rdsr(dev, &status);
|
||||
if (err) {
|
||||
LOG_ERR("Could not read status register (err %d)", err);
|
||||
|
@ -327,8 +329,11 @@ static int eeprom_at25_wait_for_idle(struct device *dev)
|
|||
if (!(status & EEPROM_AT25_STATUS_WIP)) {
|
||||
return 0;
|
||||
}
|
||||
if (now > timeout) {
|
||||
break;
|
||||
}
|
||||
k_sleep(K_MSEC(1));
|
||||
} while (timeout > k_uptime_get());
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue