From 5d5d88b27158ebfbe833474784f4cf9f8affcaf5 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Sat, 17 Sep 2022 17:31:37 +0200 Subject: [PATCH] drivers/mtd_flashpage: implement pagewise API --- drivers/include/mtd_flashpage.h | 7 +- drivers/mtd_flashpage/mtd_flashpage.c | 134 ++++++++++++++++++++++---- 2 files changed, 120 insertions(+), 21 deletions(-) diff --git a/drivers/include/mtd_flashpage.h b/drivers/include/mtd_flashpage.h index 137a415c9a..932efc2bcc 100644 --- a/drivers/include/mtd_flashpage.h +++ b/drivers/include/mtd_flashpage.h @@ -43,9 +43,7 @@ extern "C" .sector_count = FLASHPAGE_NUMOF, \ .pages_per_sector = _pages_per_sector, \ .page_size = FLASHPAGE_SIZE / _pages_per_sector, \ - .write_size = FLASHPAGE_WRITE_BLOCK_SIZE >= FLASHPAGE_WRITE_BLOCK_ALIGNMENT \ - ? FLASHPAGE_WRITE_BLOCK_SIZE \ - : FLASHPAGE_WRITE_BLOCK_ALIGNMENT, \ + .write_size = 1 \ }, \ } @@ -59,6 +57,9 @@ extern const mtd_desc_t mtd_flashpage_driver; */ typedef struct { mtd_dev_t base; /**< MTD generic device */ + uint32_t offset; /**< Offset in terms of MTD pages, which must comprise + a whole number of sectors from the start of the + flash */ } mtd_flashpage_t; #ifdef __cplusplus diff --git a/drivers/mtd_flashpage/mtd_flashpage.c b/drivers/mtd_flashpage/mtd_flashpage.c index 2bfc34789c..553ea8a5e1 100644 --- a/drivers/mtd_flashpage/mtd_flashpage.c +++ b/drivers/mtd_flashpage/mtd_flashpage.c @@ -16,6 +16,8 @@ * @brief Implementation for the flashpage memory driver * * @author Vincent Dupont + * @author Benjamin Valentin + * @author Fabian Hüßler * @} */ @@ -26,23 +28,38 @@ #include "architecture.h" #include "cpu.h" #include "cpu_conf.h" +#include "macros/utils.h" #include "mtd_flashpage.h" #include "periph/flashpage.h" +#define ENABLE_DEBUG 0 +#include "debug.h" + #define MTD_FLASHPAGE_END_ADDR ((uint32_t) CPU_FLASH_BASE + (FLASHPAGE_NUMOF * FLASHPAGE_SIZE)) static int _init(mtd_dev_t *dev) { - (void)dev; + mtd_flashpage_t *super = container_of(dev, mtd_flashpage_t, base); + (void)super; assert(dev->pages_per_sector * dev->page_size == FLASHPAGE_SIZE); + assert(!(super->offset % dev->pages_per_sector)); + + assert((int)flashpage_addr(super->offset / dev->pages_per_sector) >= (int)CPU_FLASH_BASE); + assert((uintptr_t)flashpage_addr(super->offset / dev->pages_per_sector) + + dev->pages_per_sector * dev->page_size * dev->sector_count <= MTD_FLASHPAGE_END_ADDR); + assert((uintptr_t)flashpage_addr(super->offset / dev->pages_per_sector) + + dev->pages_per_sector * dev->page_size * dev->sector_count > CPU_FLASH_BASE); return 0; } static int _read(mtd_dev_t *dev, void *buf, uint32_t addr, uint32_t size) { - assert(addr < MTD_FLASHPAGE_END_ADDR); + mtd_flashpage_t *super = container_of(dev, mtd_flashpage_t, base); - (void)dev; + addr += (uintptr_t)flashpage_addr(super->offset); + + DEBUG("flashpage: read %"PRIu32" bytes from 0x%"PRIx32" to %p\n", size, addr, buf); + assert(addr < MTD_FLASHPAGE_END_ADDR); #ifndef CPU_HAS_UNALIGNED_ACCESS if (addr % sizeof(uword_t)) { @@ -56,9 +73,49 @@ static int _read(mtd_dev_t *dev, void *buf, uint32_t addr, uint32_t size) return 0; } +static int _read_page(mtd_dev_t *dev, void *buf, uint32_t page, + uint32_t offset, uint32_t size) +{ + mtd_flashpage_t *super = container_of(dev, mtd_flashpage_t, base); + + assert(page + super->offset >= page); + page += super->offset; + + /* mtd flashpage maps multiple pages to one virtual sector for unknown reason */ + uint32_t fpage = page / dev->pages_per_sector; + offset += (page % dev->pages_per_sector) * dev->page_size; + uintptr_t addr = (uintptr_t)flashpage_addr(fpage); + + addr += offset; + + DEBUG("flashpage: read %"PRIu32" bytes from %p to %p\n", size, (void *)addr, buf); + +#ifndef CPU_HAS_UNALIGNED_ACCESS + if (addr % sizeof(uword_t)) { + uword_t tmp; + + offset = addr % sizeof(uword_t); + size = MIN(size, sizeof(uword_t) - offset); + + DEBUG("flashpage: read %"PRIu32" unaligned bytes\n", size); + + memcpy(&tmp, (uint8_t *)addr - offset, sizeof(tmp)); + memcpy(buf, (uint8_t *)&tmp + offset, size); + return size; + } +#endif + + memcpy(buf, (void *)addr, size); + return size; +} + static int _write(mtd_dev_t *dev, const void *buf, uint32_t addr, uint32_t size) { - (void)dev; + mtd_flashpage_t *super = container_of(dev, mtd_flashpage_t, base); + + addr += (uintptr_t)flashpage_addr(super->offset); + + DEBUG("flashpage: write %"PRIu32" bytes from %p to 0x%"PRIx32"\n", size, buf, addr); #ifndef CPU_HAS_UNALIGNED_ACCESS if ((uintptr_t)buf % sizeof(uword_t)) { @@ -81,24 +138,63 @@ static int _write(mtd_dev_t *dev, const void *buf, uint32_t addr, uint32_t size) return 0; } -int _erase(mtd_dev_t *dev, uint32_t addr, uint32_t size) +static int _write_page(mtd_dev_t *dev, const void *buf, uint32_t page, uint32_t offset, + uint32_t size) { - size_t sector_size = dev->page_size * dev->pages_per_sector; + mtd_flashpage_t *super = container_of(dev, mtd_flashpage_t, base); - if (size % sector_size) { - return -EOVERFLOW; - } - if (addr + size > MTD_FLASHPAGE_END_ADDR) { - return -EOVERFLOW; - } - if (addr % sector_size) { - return -EOVERFLOW; + assert(page + super->offset >= page); + + page += super->offset; + + /* mtd flashpage maps multiple pages to one virtual sector for unknown reason */ + uint32_t fpage = page / dev->pages_per_sector; + offset += (page % dev->pages_per_sector) * dev->page_size; + uintptr_t addr = (uintptr_t)flashpage_addr(fpage); + + addr += offset; + + DEBUG("flashpage: write %"PRIu32" bytes from %p to %p\n", size, buf, (void *)addr); + + size = MIN(flashpage_size(fpage) - offset, size); + + if ((addr % FLASHPAGE_WRITE_BLOCK_ALIGNMENT) || (size < FLASHPAGE_WRITE_BLOCK_SIZE) || + ((uintptr_t)buf % FLASHPAGE_WRITE_BLOCK_ALIGNMENT)) { + uint8_t tmp[FLASHPAGE_WRITE_BLOCK_SIZE] + __attribute__ ((aligned (FLASHPAGE_WRITE_BLOCK_ALIGNMENT))); + + offset = addr % FLASHPAGE_WRITE_BLOCK_ALIGNMENT; + size = MIN(size, FLASHPAGE_WRITE_BLOCK_ALIGNMENT - offset); + + DEBUG("flashpage: write %"PRIu32" unaligned bytes\n", size); + + memcpy(&tmp[0], (uint8_t *)addr - offset, sizeof(tmp)); + memcpy(&tmp[offset], buf, size); + + flashpage_write((uint8_t *)addr - offset, tmp, sizeof(tmp)); + + return size; } - uword_t dst_addr = addr; + /* don't write less than the write block size */ + size &= ~(FLASHPAGE_WRITE_BLOCK_SIZE - 1); - for (size_t i = 0; i < size; i += sector_size) { - flashpage_erase(flashpage_page((void *)(dst_addr + i))); + flashpage_write((void *)addr, buf, size); + return size; +} + +static int _erase_sector(mtd_dev_t *dev, uint32_t sector, uint32_t count) +{ + mtd_flashpage_t *super = container_of(dev, mtd_flashpage_t, base); + + if (sector + (super->offset / dev->pages_per_sector) < sector) { + return -EOVERFLOW; + } + sector += (super->offset / dev->pages_per_sector); + + while (count--) { + DEBUG("flashpage: erase sector %"PRIu32"\n", sector); + flashpage_erase(sector++); } return 0; @@ -107,6 +203,8 @@ int _erase(mtd_dev_t *dev, uint32_t addr, uint32_t size) const mtd_desc_t mtd_flashpage_driver = { .init = _init, .read = _read, + .read_page = _read_page, .write = _write, - .erase = _erase, + .write_page = _write_page, + .erase_sector = _erase_sector, };