diff --git a/drivers/at25xxx/at25xxx.c b/drivers/at25xxx/at25xxx.c index 33509b9cf0..7e53b8831c 100644 --- a/drivers/at25xxx/at25xxx.c +++ b/drivers/at25xxx/at25xxx.c @@ -26,6 +26,7 @@ #include "at25xxx.h" #include "at25xxx_constants.h" #include "at25xxx_params.h" +#include "bitarithm.h" #include "byteorder.h" #include "xtimer.h" @@ -81,12 +82,14 @@ static inline int _wait_until_eeprom_ready(const at25xxx_t *dev) return tries == 0 ? -ETIMEDOUT : 0; } -static ssize_t _write_page(const at25xxx_t *dev, uint32_t pos, const void *data, size_t len) +static int _at25xxx_write_page(const at25xxx_t *dev, uint32_t page, uint32_t offset, const void *data, size_t len) { + assert(offset < PAGE_SIZE); + /* write no more than to the end of the current page to prevent wrap-around */ - size_t remaining = PAGE_SIZE - (pos & (PAGE_SIZE - 1)); + size_t remaining = PAGE_SIZE - offset; len = min(len, remaining); - pos = _pos(CMD_WRITE, pos); + uint32_t pos = _pos(CMD_WRITE, page * PAGE_SIZE + offset); /* wait for previous write to finish - may take up to 5 ms */ int res = _wait_until_eeprom_ready(dev); @@ -111,6 +114,17 @@ static ssize_t _write_page(const at25xxx_t *dev, uint32_t pos, const void *data, return len; } +int at25xxx_write_page(const at25xxx_t *dev, uint32_t page, uint32_t offset, const void *data, size_t len) +{ + int res; + + getbus(dev); + res = _at25xxx_write_page(dev, page, offset, data, len); + spi_release(dev->params.spi); + + return res; +} + int at25xxx_write(const at25xxx_t *dev, uint32_t pos, const void *data, size_t len) { int res = 0; @@ -120,18 +134,32 @@ int at25xxx_write(const at25xxx_t *dev, uint32_t pos, const void *data, size_t l return -ERANGE; } + /* page size is always a power of two */ + const uint32_t page_shift = bitarithm_msb(PAGE_SIZE); + const uint32_t page_mask = PAGE_SIZE - 1; + + uint32_t page = pos >> page_shift; + uint32_t offset = pos & page_mask; + getbus(dev); while (len) { - ssize_t written = _write_page(dev, pos, d, len); + ssize_t written = _at25xxx_write_page(dev, page, offset, d, len); + if (written < 0) { res = written; break; } len -= written; - pos += written; - d += written; + + if (len == 0) { + break; + } + + d += written; + page += (offset + written) >> page_shift; + offset = (offset + written) & page_mask; } spi_release(dev->params.spi); @@ -177,7 +205,6 @@ uint8_t at25xxx_read_byte(const at25xxx_t *dev, uint32_t pos) int at25xxx_set(const at25xxx_t *dev, uint32_t pos, uint8_t val, size_t len) { uint8_t data[AT225XXXX_SET_BUF_SIZE]; - size_t total = 0; if (pos + len > dev->params.size) { return -ERANGE; @@ -185,13 +212,20 @@ int at25xxx_set(const at25xxx_t *dev, uint32_t pos, uint8_t val, size_t len) memset(data, val, sizeof(data)); + /* page size is always a power of two */ + const uint32_t page_shift = bitarithm_msb(PAGE_SIZE); + const uint32_t page_mask = PAGE_SIZE - 1; + + uint32_t page = pos >> page_shift; + uint32_t offset = pos & page_mask; + getbus(dev); while (len) { - size_t written = _write_page(dev, pos, data, min(len, sizeof(data))); - len -= written; - pos += written; - total += written; + size_t written = _at25xxx_write_page(dev, page, offset, data, min(len, sizeof(data))); + len -= written; + page += (offset + written) >> page_shift; + offset = (offset + written) & page_mask; } spi_release(dev->params.spi); diff --git a/drivers/at25xxx/mtd/mtd.c b/drivers/at25xxx/mtd/mtd.c index 10b6dc2244..1c70573b4d 100644 --- a/drivers/at25xxx/mtd/mtd.c +++ b/drivers/at25xxx/mtd/mtd.c @@ -57,6 +57,15 @@ static int mtd_at25xxx_write(mtd_dev_t *dev, const void *buff, uint32_t addr, ui return at25xxx_write(mtd_at25xxx_->at25xxx_eeprom, addr, buff, size); } +static int mtd_at25xxx_write_page(mtd_dev_t *dev, const void *src, uint32_t page, uint32_t offset, + uint32_t size) +{ + DEBUG("[mtd_at25xxx] write_page: page:%" PRIu32 " offset:%" PRIu32 " size:%" PRIu32 "\n", + page, offset, size); + mtd_at25xxx_t *mtd_at25xxx_ = (mtd_at25xxx_t*)dev; + return at25xxx_write_page(mtd_at25xxx_->at25xxx_eeprom, page, offset, src, size); +} + static int mtd_at25xxx_erase(mtd_dev_t *dev, uint32_t addr, uint32_t size) { DEBUG("[mtd_at25xxx] mtd_at25xxx_erase: addr:%" PRIu32 " size:%" PRIu32 "\n", addr, size); @@ -78,6 +87,7 @@ const mtd_desc_t mtd_at25xxx_driver = { .init = mtd_at25xxx_init, .read = mtd_at25xxx_read, .write = mtd_at25xxx_write, + .write_page = mtd_at25xxx_write_page, .erase = mtd_at25xxx_erase, .power = mtd_at25xxx_power, }; diff --git a/drivers/include/at25xxx.h b/drivers/include/at25xxx.h index e4bd598234..7d8119defc 100644 --- a/drivers/include/at25xxx.h +++ b/drivers/include/at25xxx.h @@ -109,6 +109,22 @@ void at25xxx_write_byte(const at25xxx_t *dev, uint32_t pos, uint8_t data); */ int at25xxx_write(const at25xxx_t *dev, uint32_t pos, const void *data, size_t len); +/** + * @brief Sequentially write @p len bytes to a given @p page. + * The function will write up to the page boundary and then return. + * + * @param[in] dev AT25XXX device handle + * @param[in] page page of EEPROM memory + * @param[in] offset offset from the start of the page, must be < page size + * @param[in] data write buffer + * @param[in] len requested length to be written + * + * @return number of bytes written on success + * @return error on failure + */ +int at25xxx_write_page(const at25xxx_t *dev, uint32_t page, uint32_t offset, + const void *data, size_t len); + /** * @brief Set @p len bytes from a given position @p pos to the * value @p val