Merge pull request #14038 from benpicco/mtd_pagewise

mtd: add page addressed operations to allow access > 4GiB on SD cards
This commit is contained in:
Alexandre Abadie 2020-08-25 13:10:20 +02:00 committed by GitHub
commit 9b2aa40e03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 551 additions and 124 deletions

View File

@ -58,8 +58,8 @@
/* the external pointer to the system MTD device */ /* the external pointer to the system MTD device */
mtd_dev_t* mtd0 = 0; mtd_dev_t* mtd0 = 0;
mtd_dev_t _flash_dev; static mtd_dev_t _flash_dev;
mtd_desc_t _flash_driver; static mtd_desc_t _flash_driver;
#ifdef MCU_ESP8266 #ifdef MCU_ESP8266
@ -77,6 +77,8 @@ extern uint32_t spi_flash_get_id(void);
static int _flash_init (mtd_dev_t *dev); static int _flash_init (mtd_dev_t *dev);
static int _flash_read (mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size); static int _flash_read (mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size);
static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size); static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size);
static int _flash_write_page (mtd_dev_t *dev, const void *buff, uint32_t page,
uint32_t offset, uint32_t size);
static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size); static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size);
static int _flash_power (mtd_dev_t *dev, enum mtd_power_state power); static int _flash_power (mtd_dev_t *dev, enum mtd_power_state power);
@ -120,6 +122,7 @@ void spi_flash_drive_init (void)
_flash_driver.init = &_flash_init; _flash_driver.init = &_flash_init;
_flash_driver.read = &_flash_read; _flash_driver.read = &_flash_read;
_flash_driver.write = &_flash_write; _flash_driver.write = &_flash_write;
_flash_driver.write_page = &_flash_write_page;
_flash_driver.erase = &_flash_erase; _flash_driver.erase = &_flash_erase;
_flash_driver.power = &_flash_power; _flash_driver.power = &_flash_power;
@ -528,6 +531,16 @@ static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32
return (spi_flash_write(_flash_beg + addr, buff, size) == ESP_OK) ? 0 : -EIO; return (spi_flash_write(_flash_beg + addr, buff, size) == ESP_OK) ? 0 : -EIO;
} }
static int _flash_write_page (mtd_dev_t *dev, const void *buff, uint32_t page, uint32_t offset,
uint32_t size)
{
uint32_t addr = _flash_beg + page * _flashchip->page_size + offset;
uint32_t remaining = _flashchip->page_size - offset;
size = MIN(size, remaining);
return (spi_flash_write(addr, buff, size) == ESP_OK) ? (int) size : -EIO;
}
static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size) static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size)
{ {
DEBUG("%s dev=%p addr=%08x size=%u\n", __func__, dev, addr, size); DEBUG("%s dev=%p addr=%08x size=%u\n", __func__, dev, addr, size);

View File

@ -104,17 +104,14 @@ int _read(const at24cxxx_t *dev, uint32_t pos, void *data, size_t len)
} }
static static
int _write(const at24cxxx_t *dev, uint32_t pos, const void *data, size_t len) int _write_page(const at24cxxx_t *dev, uint32_t pos, const void *data, size_t len)
{ {
int check = 0; int check;
const uint8_t *cdata = ((const uint8_t *)data);
while (len) {
size_t clen = MIN(len, DEV_PAGE_SIZE - MOD_POW2(pos, DEV_PAGE_SIZE));
uint8_t polls = DEV_MAX_POLLS; uint8_t polls = DEV_MAX_POLLS;
uint8_t dev_addr; uint8_t dev_addr;
uint16_t _pos; uint16_t _pos;
uint8_t flags = 0; uint8_t flags = 0;
if (DEV_EEPROM_SIZE > 2048) { if (DEV_EEPROM_SIZE > 2048) {
/* 2 bytes word address length if more than 11 bits are /* 2 bytes word address length if more than 11 bits are
used for addressing */ used for addressing */
@ -128,14 +125,31 @@ int _write(const at24cxxx_t *dev, uint32_t pos, const void *data, size_t len)
dev_addr = (DEV_I2C_ADDR | ((pos & 0xFF00) >> 8)); dev_addr = (DEV_I2C_ADDR | ((pos & 0xFF00) >> 8));
_pos = pos & 0xFF; _pos = pos & 0xFF;
} }
while (-ENXIO == (check = i2c_write_regs(DEV_I2C_BUS, dev_addr, while (-ENXIO == (check = i2c_write_regs(DEV_I2C_BUS, dev_addr,
_pos, cdata, clen, flags))) { _pos, data, len, flags))) {
if (--polls == 0) { if (--polls == 0) {
break; break;
} }
xtimer_usleep(AT24CXXX_POLL_DELAY_US); xtimer_usleep(AT24CXXX_POLL_DELAY_US);
} }
DEBUG("[at24cxxx] i2c_write_regs(): %d; polls: %d\n", check, polls); DEBUG("[at24cxxx] i2c_write_regs(): %d; polls: %d\n", check, polls);
return check;
}
static
int _write(const at24cxxx_t *dev, uint32_t pos, const void *data, size_t len)
{
int check = 0;
const uint8_t *cdata = ((const uint8_t *)data);
while (len) {
size_t clen = MIN(len, DEV_PAGE_SIZE - MOD_POW2(pos, DEV_PAGE_SIZE));
check = _write_page(dev, pos, cdata, clen);
if (!check) { if (!check) {
len -= clen; len -= clen;
pos += clen; pos += clen;
@ -243,6 +257,24 @@ int at24cxxx_write(const at24cxxx_t *dev, uint32_t pos, const void *data,
return check; return check;
} }
int at24cxxx_write_page(const at24cxxx_t *dev, uint32_t page, uint32_t offset,
const void *data, size_t len)
{
int check;
assert(offset < DEV_PAGE_SIZE);
/* write no more than to the end of the current page to prevent wrap-around */
size_t remaining = DEV_PAGE_SIZE - offset;
len = MIN(len, remaining);
i2c_acquire(DEV_I2C_BUS);
check = _write_page(dev, page * DEV_PAGE_SIZE + offset, data, len);
i2c_release(DEV_I2C_BUS);
return check ? check : (int) len;
}
int at24cxxx_set(const at24cxxx_t *dev, uint32_t pos, uint8_t val, int at24cxxx_set(const at24cxxx_t *dev, uint32_t pos, uint8_t val,
size_t len) size_t len)
{ {

View File

@ -56,6 +56,12 @@ static int _mtd_at24cxxx_write(mtd_dev_t *mtd, const void *src, uint32_t addr,
return at24cxxx_write(DEV(mtd), addr, src, size) == AT24CXXX_OK ? 0 : -EIO; return at24cxxx_write(DEV(mtd), addr, src, size) == AT24CXXX_OK ? 0 : -EIO;
} }
static int mtd_at24cxxx_write_page(mtd_dev_t *mtd, const void *src, uint32_t page,
uint32_t offset, uint32_t size)
{
return at24cxxx_write_page(DEV(mtd), page, offset, src, size);
}
static int _mtd_at24cxxx_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size) static int _mtd_at24cxxx_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size)
{ {
return at24cxxx_clear(DEV(mtd), addr, size) == AT24CXXX_OK ? 0 : -EIO; return at24cxxx_clear(DEV(mtd), addr, size) == AT24CXXX_OK ? 0 : -EIO;
@ -72,6 +78,7 @@ const mtd_desc_t mtd_at24cxxx_driver = {
.init = _mtd_at24cxxx_init, .init = _mtd_at24cxxx_init,
.read = _mtd_at24cxxx_read, .read = _mtd_at24cxxx_read,
.write = _mtd_at24cxxx_write, .write = _mtd_at24cxxx_write,
.write_page = mtd_at24cxxx_write_page,
.erase = _mtd_at24cxxx_erase, .erase = _mtd_at24cxxx_erase,
.power = _mtd_at24cxxx_power .power = _mtd_at24cxxx_power
}; };

View File

@ -26,6 +26,7 @@
#include "at25xxx.h" #include "at25xxx.h"
#include "at25xxx_constants.h" #include "at25xxx_constants.h"
#include "at25xxx_params.h" #include "at25xxx_params.h"
#include "bitarithm.h"
#include "byteorder.h" #include "byteorder.h"
#include "xtimer.h" #include "xtimer.h"
@ -81,12 +82,14 @@ static inline int _wait_until_eeprom_ready(const at25xxx_t *dev)
return tries == 0 ? -ETIMEDOUT : 0; 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 */ /* 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); 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 */ /* wait for previous write to finish - may take up to 5 ms */
int res = _wait_until_eeprom_ready(dev); 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; 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 at25xxx_write(const at25xxx_t *dev, uint32_t pos, const void *data, size_t len)
{ {
int res = 0; 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; 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); getbus(dev);
while (len) { 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) { if (written < 0) {
res = written; res = written;
break; break;
} }
len -= written; len -= written;
pos += written;
if (len == 0) {
break;
}
d += written; d += written;
page += (offset + written) >> page_shift;
offset = (offset + written) & page_mask;
} }
spi_release(dev->params.spi); 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) int at25xxx_set(const at25xxx_t *dev, uint32_t pos, uint8_t val, size_t len)
{ {
uint8_t data[AT225XXXX_SET_BUF_SIZE]; uint8_t data[AT225XXXX_SET_BUF_SIZE];
size_t total = 0;
if (pos + len > dev->params.size) { if (pos + len > dev->params.size) {
return -ERANGE; 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)); 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); getbus(dev);
while (len) { while (len) {
size_t written = _write_page(dev, pos, data, min(len, sizeof(data))); size_t written = _at25xxx_write_page(dev, page, offset, data, min(len, sizeof(data)));
len -= written; len -= written;
pos += written; page += (offset + written) >> page_shift;
total += written; offset = (offset + written) & page_mask;
} }
spi_release(dev->params.spi); spi_release(dev->params.spi);

View File

@ -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); 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) 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); 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, .init = mtd_at25xxx_init,
.read = mtd_at25xxx_read, .read = mtd_at25xxx_read,
.write = mtd_at25xxx_write, .write = mtd_at25xxx_write,
.write_page = mtd_at25xxx_write_page,
.erase = mtd_at25xxx_erase, .erase = mtd_at25xxx_erase,
.power = mtd_at25xxx_power, .power = mtd_at25xxx_power,
}; };

View File

@ -129,6 +129,23 @@ int at24cxxx_write_byte(const at24cxxx_t *dev, uint32_t pos, uint8_t data);
int at24cxxx_write(const at24cxxx_t *dev, uint32_t pos, const void *data, int at24cxxx_write(const at24cxxx_t *dev, uint32_t pos, const void *data,
size_t len); 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
* the number of bytes written up to that.
*
* @param[in] dev AT24CXXX 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 at24cxxx_write_page(const at24cxxx_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 * @brief Set @p len bytes from a given position @p pos to the
* value @p val * value @p val

View File

@ -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); 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 * @brief Set @p len bytes from a given position @p pos to the
* value @p val * value @p val

View File

@ -101,6 +101,27 @@ struct mtd_desc {
uint32_t addr, uint32_t addr,
uint32_t size); uint32_t size);
/**
* @brief Read from the Memory Technology Device (MTD) using
* pagewise addressing.
*
* @p offset should not exceed the page size
*
* @param[in] dev Pointer to the selected driver
* @param[out] buff Pointer to the data buffer to store read data
* @param[in] page Page number to start reading from
* @param[in] offset Byte offset from the start of the page
* @param[in] size Number of bytes
*
* @return number of bytes read on success
* @return < 0 value on error
*/
int (*read_page)(mtd_dev_t *dev,
void *buff,
uint32_t page,
uint32_t offset,
uint32_t size);
/** /**
* @brief Write to the Memory Technology Device (MTD) * @brief Write to the Memory Technology Device (MTD)
* *
@ -120,6 +141,27 @@ struct mtd_desc {
uint32_t addr, uint32_t addr,
uint32_t size); uint32_t size);
/**
* @brief Write to the Memory Technology Device (MTD) using
* pagewise addressing.
*
* @p offset should not exceed the page size
*
* @param[in] dev Pointer to the selected driver
* @param[out] buff Pointer to the data to be written
* @param[in] page Page number to start writing to
* @param[in] offset Byte offset from the start of the page
* @param[in] size Number of bytes
*
* @return bytes written on success
* @return < 0 value on error
*/
int (*write_page)(mtd_dev_t *dev,
const void *buff,
uint32_t page,
uint32_t offset,
uint32_t size);
/** /**
* @brief Erase sector(s) over the Memory Technology Device (MTD) * @brief Erase sector(s) over the Memory Technology Device (MTD)
* *
@ -136,6 +178,21 @@ struct mtd_desc {
uint32_t addr, uint32_t addr,
uint32_t size); uint32_t size);
/**
* @brief Erase sector(s) of the Memory Technology Device (MTD)
*
* @param[in] dev Pointer to the selected driver
* @param[in] sector the first sector number to erase
* @param[in] count Number of sectors to erase
*
* @return 0 on success
* @return < 0 value on error
*/
int (*erase_sector)(mtd_dev_t *dev,
uint32_t sector,
uint32_t count);
/** /**
* @brief Control power of Memory Technology Device (MTD) * @brief Control power of Memory Technology Device (MTD)
* *
@ -176,6 +233,29 @@ int mtd_init(mtd_dev_t *mtd);
*/ */
int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count); int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count);
/**
* @brief Read data from a MTD device with pagewise addressing
*
* The MTD layer will take care of splitting up the transaction into multiple
* reads if it is required by the underlying storage media.
*
* @p offset must be smaller than the page size
*
* @param mtd the device to read from
* @param[out] dest the buffer to fill in
* @param[in] page Page number to start reading from
* @param[in] offset offset from the start of the page (in bytes)
* @param[in] size the number of bytes to read
*
* @return 0 on success
* @return < 0 if an error occurred
* @return -ENODEV if @p mtd is not a valid device
* @return -ENOTSUP if operation is not supported on @p mtd
* @return -EOVERFLOW if @p addr or @p count are not valid, i.e. outside memory
* @return -EIO if I/O error occurred
*/
int mtd_read_page(mtd_dev_t *mtd, void *dest, uint32_t page, uint32_t offset, uint32_t size);
/** /**
* @brief Write data to a MTD device * @brief Write data to a MTD device
* *
@ -199,6 +279,31 @@ int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count);
*/ */
int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count); int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count);
/**
* @brief Write data to a MTD device with pagewise addressing
*
* The MTD layer will take care of splitting up the transaction into multiple
* writes if it is required by the underlying storage media.
*
* @p offset must be smaller than the page size
*
*
* @param mtd the device to write to
* @param[in] src the buffer to write
* @param[in] page Page number to start writing to
* @param[in] offset byte offset from the start of the page
* @param[in] size the number of bytes to write
*
* @return 0 on success
* @return < 0 if an error occurred
* @return -ENODEV if @p mtd is not a valid device
* @return -ENOTSUP if operation is not supported on @p mtd
* @return -EOVERFLOW if @p addr or @p count are not valid, i.e. outside memory,
* @return -EIO if I/O error occurred
* @return -EINVAL if parameters are invalid
*/
int mtd_write_page(mtd_dev_t *mtd, const void *src, uint32_t page, uint32_t offset, uint32_t size);
/** /**
* @brief Erase sectors of a MTD device * @brief Erase sectors of a MTD device
* *
@ -217,6 +322,22 @@ int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count);
*/ */
int mtd_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count); int mtd_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count);
/**
* @brief Erase sectors of a MTD device
*
* @param mtd the device to erase
* @param[in] sector the first sector number to erase
* @param[in] num the number of sectors to erase
*
* @return 0 if erase successful
* @return < 0 if an error occurred
* @return -ENODEV if @p mtd is not a valid device
* @return -ENOTSUP if operation is not supported on @p mtd
* @return -EOVERFLOW if @p addr or @p sector are not valid, i.e. outside memory
* @return -EIO if I/O error occurred
*/
int mtd_erase_sector(mtd_dev_t *mtd, uint32_t sector, uint32_t num);
/** /**
* @brief Set power mode on a MTD device * @brief Set power mode on a MTD device
* *

View File

@ -18,8 +18,12 @@
* @author Vincent Dupont <vincent@otakeys.com> * @author Vincent Dupont <vincent@otakeys.com>
*/ */
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <limits.h>
#include <stddef.h>
#include "bitarithm.h"
#include "mtd.h" #include "mtd.h"
int mtd_init(mtd_dev_t *mtd) int mtd_init(mtd_dev_t *mtd)
@ -42,42 +46,172 @@ int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count)
return -ENODEV; return -ENODEV;
} }
if (mtd->driver->read) { /* page size is always a power of two */
return mtd->driver->read(mtd, dest, addr, count); const uint32_t page_shift = bitarithm_msb(mtd->page_size);
const uint32_t page_mask = mtd->page_size - 1;
return mtd_read_page(mtd, dest, addr >> page_shift, addr & page_mask, count);
} }
else {
int mtd_read_page(mtd_dev_t *mtd, void *dest, uint32_t page, uint32_t offset,
uint32_t count)
{
if (!mtd || !mtd->driver) {
return -ENODEV;
}
if (mtd->driver->read_page == NULL) {
/* TODO: remove when all backends implement read_page */
if (mtd->driver->read) {
return mtd->driver->read(mtd, dest, mtd->page_size * page + offset, count);
} else {
return -ENOTSUP; return -ENOTSUP;
} }
} }
/* Implementation assumes page size is <= INT_MAX and a power of two. */
/* We didn't find hardware yet where this is not true. */
assert(mtd->page_size <= INT_MAX);
assert(bitarithm_bits_set(mtd->page_size) == 1);
/* page size is always a power of two */
const uint32_t page_shift = bitarithm_msb(mtd->page_size);
const uint32_t page_mask = mtd->page_size - 1;
page += offset >> page_shift;
offset = offset & page_mask;
char *_dst = dest;
while (count) {
int read_bytes = mtd->driver->read_page(mtd, _dst, page, offset, count);
if (read_bytes < 0) {
return read_bytes;
}
count -= read_bytes;
if (count == 0) {
break;
}
_dst += read_bytes;
page += (offset + read_bytes) >> page_shift;
offset = (offset + read_bytes) & page_mask;
}
return 0;
}
int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count) int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count)
{ {
if (!mtd || !mtd->driver) { if (!mtd || !mtd->driver) {
return -ENODEV; return -ENODEV;
} }
if (mtd->driver->write) { /* page size is always a power of two */
return mtd->driver->write(mtd, src, addr, count); const uint32_t page_shift = bitarithm_msb(mtd->page_size);
const uint32_t page_mask = mtd->page_size - 1;
return mtd_write_page(mtd, src, addr >> page_shift, addr & page_mask, count);
} }
else {
int mtd_write_page(mtd_dev_t *mtd, const void *src, uint32_t page, uint32_t offset,
uint32_t count)
{
if (!mtd || !mtd->driver) {
return -ENODEV;
}
if (mtd->driver->write_page == NULL) {
/* TODO: remove when all backends implement write_page */
if (mtd->driver->write) {
return mtd->driver->write(mtd, src, mtd->page_size * page + offset, count);
} else {
return -ENOTSUP; return -ENOTSUP;
} }
} }
/* Implementation assumes page size is <= INT_MAX and a power of two. */
/* We didn't find hardware yet where this is not true. */
assert(mtd->page_size <= INT_MAX);
assert(bitarithm_bits_set(mtd->page_size) == 1);
/* page size is always a power of two */
const uint32_t page_shift = bitarithm_msb(mtd->page_size);
const uint32_t page_mask = mtd->page_size - 1;
page += offset >> page_shift;
offset = offset & page_mask;
const char *_src = src;
while (count) {
int written = mtd->driver->write_page(mtd, _src, page, offset, count);
if (written < 0) {
return written;
}
count -= written;
if (count == 0) {
break;
}
_src += written;
page += (offset + written) >> page_shift;
offset = (offset + written) & page_mask;
}
return 0;
}
int mtd_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count) int mtd_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count)
{ {
if (!mtd || !mtd->driver) { if (!mtd || !mtd->driver) {
return -ENODEV; return -ENODEV;
} }
if (mtd->driver->erase) { uint32_t sector_size = mtd->pages_per_sector * mtd->page_size;
return mtd->driver->erase(mtd, addr, count);
if (count % sector_size) {
return -EOVERFLOW;
} }
else {
if (addr % sector_size) {
return -EOVERFLOW;
}
return mtd_erase_sector(mtd, addr / sector_size, count / sector_size);
}
int mtd_erase_sector(mtd_dev_t *mtd, uint32_t sector, uint32_t count)
{
if (!mtd || !mtd->driver) {
return -ENODEV;
}
if (sector >= mtd->sector_count) {
return -EOVERFLOW;
}
if (mtd->driver->erase_sector == NULL) {
/* TODO: remove when all backends implement erase_sector */
if (mtd->driver->erase) {
uint32_t sector_size = mtd->pages_per_sector * mtd->page_size;
return mtd->driver->erase(mtd,
sector * sector_size,
count * sector_size);
} else {
return -ENOTSUP; return -ENOTSUP;
} }
} }
return mtd->driver->erase_sector(mtd, sector, count);
}
int mtd_power(mtd_dev_t *mtd, enum mtd_power_state power) int mtd_power(mtd_dev_t *mtd, enum mtd_power_state power)
{ {
if (!mtd || !mtd->driver) { if (!mtd || !mtd->driver) {

View File

@ -29,22 +29,6 @@
#include <inttypes.h> #include <inttypes.h>
#include <errno.h> #include <errno.h>
static int mtd_sdcard_init(mtd_dev_t *mtd);
static int mtd_sdcard_read(mtd_dev_t *mtd, void *dest, uint32_t addr,
uint32_t size);
static int mtd_sdcard_write(mtd_dev_t *mtd, const void *src, uint32_t addr,
uint32_t size);
static int mtd_sdcard_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size);
static int mtd_sdcard_power(mtd_dev_t *mtd, enum mtd_power_state power);
const mtd_desc_t mtd_sdcard_driver = {
.init = mtd_sdcard_init,
.read = mtd_sdcard_read,
.write = mtd_sdcard_write,
.erase = mtd_sdcard_erase,
.power = mtd_sdcard_power,
};
static int mtd_sdcard_init(mtd_dev_t *dev) static int mtd_sdcard_init(mtd_dev_t *dev)
{ {
DEBUG("mtd_sdcard_init\n"); DEBUG("mtd_sdcard_init\n");
@ -79,6 +63,28 @@ static int mtd_sdcard_read(mtd_dev_t *dev, void *buff, uint32_t addr,
return -EIO; return -EIO;
} }
static int mtd_sdcard_read_page(mtd_dev_t *dev, void *buff, uint32_t page,
uint32_t offset, uint32_t size)
{
DEBUG("mtd_sdcard_read_page: page:%" PRIu32 " offset:%" PRIu32 " size:%" PRIu32 "\n",
page, offset, size);
if (offset) {
return -ENOTSUP;
}
mtd_sdcard_t *mtd_sd = (mtd_sdcard_t*)dev;
sd_rw_response_t err;
int res = sdcard_spi_read_blocks(mtd_sd->sd_card, page,
buff, SD_HC_BLOCK_SIZE,
size / SD_HC_BLOCK_SIZE, &err);
if (err != SD_RW_OK) {
return -EIO;
}
return res * SD_HC_BLOCK_SIZE;
}
static int mtd_sdcard_write(mtd_dev_t *dev, const void *buff, uint32_t addr, static int mtd_sdcard_write(mtd_dev_t *dev, const void *buff, uint32_t addr,
uint32_t size) uint32_t size)
{ {
@ -95,6 +101,28 @@ static int mtd_sdcard_write(mtd_dev_t *dev, const void *buff, uint32_t addr,
return -EIO; return -EIO;
} }
static int mtd_sdcard_write_page(mtd_dev_t *dev, const void *buff, uint32_t page,
uint32_t offset, uint32_t size)
{
DEBUG("mtd_sdcard_write_page: page:%" PRIu32 " offset:%" PRIu32 " size:%" PRIu32 "\n",
page, offset, size);
if (offset) {
return -ENOTSUP;
}
mtd_sdcard_t *mtd_sd = (mtd_sdcard_t*)dev;
sd_rw_response_t err;
int res = sdcard_spi_write_blocks(mtd_sd->sd_card, page,
buff, SD_HC_BLOCK_SIZE,
size / SD_HC_BLOCK_SIZE, &err);
if (err != SD_RW_OK) {
return -EIO;
}
return res * SD_HC_BLOCK_SIZE;
}
static int mtd_sdcard_erase(mtd_dev_t *dev, static int mtd_sdcard_erase(mtd_dev_t *dev,
uint32_t addr, uint32_t addr,
uint32_t size) uint32_t size)
@ -121,3 +149,13 @@ static int mtd_sdcard_power(mtd_dev_t *dev, enum mtd_power_state power)
(make use of sdcard_spi_params_t.power pin) */ (make use of sdcard_spi_params_t.power pin) */
return -ENOTSUP; /* currently not supported */ return -ENOTSUP; /* currently not supported */
} }
const mtd_desc_t mtd_sdcard_driver = {
.init = mtd_sdcard_init,
.read = mtd_sdcard_read,
.read_page = mtd_sdcard_read_page,
.write = mtd_sdcard_write,
.write_page = mtd_sdcard_write_page,
.erase = mtd_sdcard_erase,
.power = mtd_sdcard_power,
};

View File

@ -56,6 +56,8 @@
#define MBIT_AS_BYTES ((1024 * 1024) / 8) #define MBIT_AS_BYTES ((1024 * 1024) / 8)
#define MIN(a, b) ((a) > (b) ? (b) : (a))
/** /**
* @brief JEDEC memory manufacturer ID codes. * @brief JEDEC memory manufacturer ID codes.
* *
@ -75,14 +77,6 @@ static int mtd_spi_nor_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uin
static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size); static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size);
static int mtd_spi_nor_power(mtd_dev_t *mtd, enum mtd_power_state power); static int mtd_spi_nor_power(mtd_dev_t *mtd, enum mtd_power_state power);
const mtd_desc_t mtd_spi_nor_driver = {
.init = mtd_spi_nor_init,
.read = mtd_spi_nor_read,
.write = mtd_spi_nor_write,
.erase = mtd_spi_nor_erase,
.power = mtd_spi_nor_power,
};
static void mtd_spi_acquire(const mtd_spi_nor_t *dev) static void mtd_spi_acquire(const mtd_spi_nor_t *dev)
{ {
spi_acquire(dev->params->spi, dev->params->cs, spi_acquire(dev->params->spi, dev->params->cs,
@ -464,7 +458,7 @@ static int mtd_spi_nor_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t
DEBUG("mtd_spi_nor_read: %p, %p, 0x%" PRIx32 ", 0x%" PRIx32 "\n", DEBUG("mtd_spi_nor_read: %p, %p, 0x%" PRIx32 ", 0x%" PRIx32 "\n",
(void *)mtd, dest, addr, size); (void *)mtd, dest, addr, size);
const mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd; const mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd;
size_t chipsize = mtd->page_size * mtd->pages_per_sector * mtd->sector_count; uint32_t chipsize = mtd->page_size * mtd->pages_per_sector * mtd->sector_count;
if (addr > chipsize) { if (addr > chipsize) {
return -EOVERFLOW; return -EOVERFLOW;
} }
@ -521,6 +515,35 @@ static int mtd_spi_nor_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uin
return 0; return 0;
} }
static int mtd_spi_nor_write_page(mtd_dev_t *mtd, const void *src, uint32_t page, uint32_t offset,
uint32_t size)
{
const mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd;
DEBUG("mtd_spi_nor_write_page: %p, %p, 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 "\n",
(void *)mtd, src, page, offset, size);
uint32_t remaining = mtd->page_size - offset;
size = MIN(remaining, size);
be_uint32_t addr_be = byteorder_htonl(page * mtd->page_size + offset);
mtd_spi_acquire(dev);
/* write enable */
mtd_spi_cmd(dev, dev->params->opcode->wren);
/* Page program */
mtd_spi_cmd_addr_write(dev, dev->params->opcode->page_program, addr_be, src, size);
/* waiting for the command to complete before returning */
wait_for_write_complete(dev, 0);
mtd_spi_release(dev);
return size;
}
static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size) static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size)
{ {
DEBUG("mtd_spi_nor_erase: %p, 0x%" PRIx32 ", 0x%" PRIx32 "\n", DEBUG("mtd_spi_nor_erase: %p, 0x%" PRIx32 ", 0x%" PRIx32 "\n",
@ -619,3 +642,12 @@ static int mtd_spi_nor_power(mtd_dev_t *mtd, enum mtd_power_state power)
return 0; return 0;
} }
const mtd_desc_t mtd_spi_nor_driver = {
.init = mtd_spi_nor_init,
.read = mtd_spi_nor_read,
.write = mtd_spi_nor_write,
.write_page = mtd_spi_nor_write_page,
.erase = mtd_spi_nor_erase,
.power = mtd_spi_nor_power,
};

View File

@ -95,15 +95,15 @@ DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
return RES_PARERR; return RES_PARERR;
} }
uint32_t nread = count * fatfs_mtd_devs[pdrv]->page_size; uint32_t sector_size = fatfs_mtd_devs[pdrv]->page_size
int res = mtd_read(fatfs_mtd_devs[pdrv], buff, * fatfs_mtd_devs[pdrv]->pages_per_sector;
sector * fatfs_mtd_devs[pdrv]->page_size,
nread); int res = mtd_read_page(fatfs_mtd_devs[pdrv], buff,
sector, 0, count * sector_size);
if (res != 0) { if (res != 0) {
return RES_ERROR; return RES_ERROR;
} }
assert((nread / fatfs_mtd_devs[pdrv]->page_size) == count);
return RES_OK; return RES_OK;
} }
@ -127,23 +127,21 @@ DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
} }
/* erase memory before writing to it */ /* erase memory before writing to it */
int res = mtd_erase(fatfs_mtd_devs[pdrv], int res = mtd_erase_sector(fatfs_mtd_devs[pdrv], sector, count);
sector * fatfs_mtd_devs[pdrv]->page_size,
count * fatfs_mtd_devs[pdrv]->page_size);
if (res != 0) { if (res != 0) {
return RES_ERROR; /* erase failed! */ return RES_ERROR; /* erase failed! */
} }
uint32_t nwrite = count * fatfs_mtd_devs[pdrv]->page_size; uint32_t sector_size = fatfs_mtd_devs[pdrv]->page_size
res = mtd_write(fatfs_mtd_devs[pdrv], buff, * fatfs_mtd_devs[pdrv]->pages_per_sector;
sector * fatfs_mtd_devs[pdrv]->page_size,
nwrite); res = mtd_write_page(fatfs_mtd_devs[pdrv], buff,
sector, 0, count * sector_size);
if (res != 0) { if (res != 0) {
return RES_ERROR; return RES_ERROR;
} }
assert((nwrite / fatfs_mtd_devs[pdrv]->page_size) == count);
return RES_OK; return RES_OK;
} }

View File

@ -72,7 +72,8 @@ static int _dev_read(const struct lfs_config *c, lfs_block_t block,
DEBUG("lfs_read: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n", DEBUG("lfs_read: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n",
(void *)c, block, off, buffer, size); (void *)c, block, off, buffer, size);
return mtd_read(mtd, buffer, ((fs->base_addr + block) * c->block_size) + off, size); return mtd_read_page(mtd, buffer, (fs->base_addr + block) * mtd->pages_per_sector,
off, size);
} }
static int _dev_write(const struct lfs_config *c, lfs_block_t block, static int _dev_write(const struct lfs_config *c, lfs_block_t block,
@ -84,17 +85,8 @@ static int _dev_write(const struct lfs_config *c, lfs_block_t block,
DEBUG("lfs_write: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n", DEBUG("lfs_write: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n",
(void *)c, block, off, buffer, size); (void *)c, block, off, buffer, size);
const uint8_t *buf = buffer; return mtd_write_page(mtd, buffer, (fs->base_addr + block) * mtd->pages_per_sector,
uint32_t addr = ((fs->base_addr + block) * c->block_size) + off; off, size);
for (const uint8_t *part = buf; part < buf + size; part += c->prog_size,
addr += c->prog_size) {
int ret = mtd_write(mtd, part, addr, c->prog_size);
if (ret != 0) {
return ret;
}
}
return 0;
} }
static int _dev_erase(const struct lfs_config *c, lfs_block_t block) static int _dev_erase(const struct lfs_config *c, lfs_block_t block)
@ -104,12 +96,7 @@ static int _dev_erase(const struct lfs_config *c, lfs_block_t block)
DEBUG("lfs_erase: c=%p, block=%" PRIu32 "\n", (void *)c, block); DEBUG("lfs_erase: c=%p, block=%" PRIu32 "\n", (void *)c, block);
int ret = mtd_erase(mtd, ((fs->base_addr + block) * c->block_size), c->block_size); return mtd_erase_sector(mtd, fs->base_addr + block, 1);
if (ret >= 0) {
return 0;
}
return ret;
} }
static int _dev_sync(const struct lfs_config *c) static int _dev_sync(const struct lfs_config *c)

View File

@ -72,7 +72,8 @@ static int _dev_read(const struct lfs_config *c, lfs_block_t block,
DEBUG("lfs_read: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n", DEBUG("lfs_read: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n",
(void *)c, block, off, buffer, size); (void *)c, block, off, buffer, size);
return mtd_read(mtd, buffer, ((fs->base_addr + block) * c->block_size) + off, size); return mtd_read_page(mtd, buffer, (fs->base_addr + block) * mtd->pages_per_sector,
off, size);
} }
static int _dev_write(const struct lfs_config *c, lfs_block_t block, static int _dev_write(const struct lfs_config *c, lfs_block_t block,
@ -84,17 +85,8 @@ static int _dev_write(const struct lfs_config *c, lfs_block_t block,
DEBUG("lfs_write: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n", DEBUG("lfs_write: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n",
(void *)c, block, off, buffer, size); (void *)c, block, off, buffer, size);
const uint8_t *buf = buffer; return mtd_write_page(mtd, buffer, (fs->base_addr + block) * mtd->pages_per_sector,
uint32_t addr = ((fs->base_addr + block) * c->block_size) + off; off, size);
for (const uint8_t *part = buf; part < buf + size; part += c->prog_size,
addr += c->prog_size) {
int ret = mtd_write(mtd, part, addr, c->prog_size);
if (ret != 0) {
return ret;
}
}
return 0;
} }
static int _dev_erase(const struct lfs_config *c, lfs_block_t block) static int _dev_erase(const struct lfs_config *c, lfs_block_t block)
@ -104,12 +96,7 @@ static int _dev_erase(const struct lfs_config *c, lfs_block_t block)
DEBUG("lfs_erase: c=%p, block=%" PRIu32 "\n", (void *)c, block); DEBUG("lfs_erase: c=%p, block=%" PRIu32 "\n", (void *)c, block);
int ret = mtd_erase(mtd, ((fs->base_addr + block) * c->block_size), c->block_size); return mtd_erase_sector(mtd, fs->base_addr + block, 1);
if (ret >= 0) {
return 0;
}
return ret;
} }
static int _dev_sync(const struct lfs_config *c) static int _dev_sync(const struct lfs_config *c)

View File

@ -7,6 +7,7 @@ BOARD_INSUFFICIENT_MEMORY := \
msb-430 \ msb-430 \
msb-430h \ msb-430h \
nucleo-f031k6 \ nucleo-f031k6 \
nucleo-l031k6 \
nucleo-f042k6 \ nucleo-f042k6 \
stm32f030f4-demo \ stm32f030f4-demo \
telosb \ telosb \