diff --git a/drivers/eeprom/eeprom_at2x.c b/drivers/eeprom/eeprom_at2x.c index defe5899cabf5..6f83eb1a7e976 100644 --- a/drivers/eeprom/eeprom_at2x.c +++ b/drivers/eeprom/eeprom_at2x.c @@ -92,7 +92,8 @@ static int eeprom_at2x_read(const struct device *dev, off_t offset, void *buf, { const struct eeprom_at2x_config *config = dev->config; struct eeprom_at2x_data *data = dev->data; - int err; + uint8_t *pbuf = buf; + int ret; if (!len) { return 0; @@ -104,14 +105,21 @@ static int eeprom_at2x_read(const struct device *dev, off_t offset, void *buf, } k_mutex_lock(&data->lock, K_FOREVER); - err = config->read_fn(dev, offset, buf, len); - k_mutex_unlock(&data->lock); + while (len) { + ret = config->read_fn(dev, offset, pbuf, len); + if (ret < 0) { + LOG_ERR("failed to read EEPROM (err %d)", ret); + k_mutex_unlock(&data->lock); + return ret; + } - if (err) { - LOG_ERR("failed to read EEPROM (err %d)", err); - return err; + pbuf += ret; + offset += ret; + len -= ret; } + k_mutex_unlock(&data->lock); + return 0; } @@ -201,6 +209,38 @@ static size_t eeprom_at2x_size(const struct device *dev) } #ifdef CONFIG_EEPROM_AT24 + +/** + * @brief translate an offset to a device address / offset pair + * + * It allows to address several devices as a continuous memory region + * but also to address higher part of eeprom for chips + * with more than 2^(addr_width) adressable word. + */ +static uint16_t eeprom_at24_translate_offset(const struct device *dev, + off_t *offset) +{ + const struct eeprom_at2x_config *config = dev->config; + + const uint16_t addr_incr = *offset >> config->addr_width; + *offset &= BIT_MASK(config->addr_width); + + return config->bus_addr + addr_incr; +} + +static size_t eeprom_at24_adjust_read_count(const struct device *dev, + off_t offset, size_t len) +{ + const struct eeprom_at2x_config *config = dev->config; + const size_t remainder = BIT(config->addr_width) - offset; + + if (len > remainder) { + len = remainder; + } + + return len; +} + static int eeprom_at24_read(const struct device *dev, off_t offset, void *buf, size_t len) { @@ -208,14 +248,19 @@ static int eeprom_at24_read(const struct device *dev, off_t offset, void *buf, struct eeprom_at2x_data *data = dev->data; int64_t timeout; uint8_t addr[2]; + uint16_t bus_addr; int err; + bus_addr = eeprom_at24_translate_offset(dev, &offset); + if (config->addr_width == 16) { sys_put_be16(offset, addr); } else { addr[0] = offset & BIT_MASK(8); } + len = eeprom_at24_adjust_read_count(dev, offset, len); + /* * A write cycle may be in progress so reads must be attempted * until the current write cycle should be completed. @@ -223,7 +268,7 @@ static int eeprom_at24_read(const struct device *dev, off_t offset, void *buf, timeout = k_uptime_get() + config->timeout; while (1) { int64_t now = k_uptime_get(); - err = i2c_write_read(data->bus_dev, config->bus_addr, + err = i2c_write_read(data->bus_dev, bus_addr, addr, config->addr_width / 8, buf, len); if (!err || now > timeout) { @@ -232,7 +277,11 @@ static int eeprom_at24_read(const struct device *dev, off_t offset, void *buf, k_sleep(K_MSEC(1)); } - return err; + if (err < 0) { + return err; + } + + return len; } static int eeprom_at24_write(const struct device *dev, off_t offset, @@ -243,9 +292,12 @@ static int eeprom_at24_write(const struct device *dev, off_t offset, int count = eeprom_at2x_limit_write_count(dev, offset, len); uint8_t block[config->addr_width / 8 + count]; int64_t timeout; + uint16_t bus_addr; int i = 0; int err; + bus_addr = eeprom_at24_translate_offset(dev, &offset); + /* * Not all I2C EEPROMs support repeated start so the the * address (offset) and data (buf) must be provided in one @@ -266,7 +318,7 @@ static int eeprom_at24_write(const struct device *dev, off_t offset, while (1) { int64_t now = k_uptime_get(); err = i2c_write(data->bus_dev, block, sizeof(block), - config->bus_addr); + bus_addr); if (!err || now > timeout) { break; } @@ -404,7 +456,12 @@ static int eeprom_at25_read(const struct device *dev, off_t offset, void *buf, return err; } - return spi_transceive(data->bus_dev, &data->spi_cfg, &tx, &rx); + err = spi_transceive(data->bus_dev, &data->spi_cfg, &tx, &rx); + if (err < 0) { + return err; + } + + return len; } static int eeprom_at25_wren(const struct device *dev)