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:
commit
9b2aa40e03
@ -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);
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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,
|
||||||
|
};
|
||||||
|
|||||||
@ -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,
|
||||||
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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 \
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user