* \author Daniele Basile <asterix@develer.com>
*
* \brief NPX lpc23xx embedded flash read/write driver.
+ *
+ * notest:arm
*/
#include "flash_lpc2.h"
#include <cpu/irq.h>
#include <cpu/attr.h>
#include <cpu/power.h>
+#include <cpu/types.h>
-#include <kern/kfile.h>
-
+#include <io/kblock.h>
#include <io/arm.h>
#include <drv/timer.h>
#include <drv/flash.h>
-#include <string.h>
+#include <struct/bitarray.h>
+#include <string.h>
+/* Embedded flash programming defines. */
#define IAP_ADDRESS 0x7ffffff1
typedef enum IapCommands
REINVOKE_ISP = 57,
} IapCommands;
+#if CPU_ARM_LPC2378
+ #define FLASH_MEM_SIZE (504 * 1024L)
+ #define FLASH_PAGE_SIZE_BYTES 4096
+ #define FLASH_REAL_PAGE_CNT 28
+#else
+ #error Unknown CPU
+#endif
#define CMD_SUCCESS 0
+struct FlashHardware
+{
+ uint8_t status;
+ int flags;
+};
+
+#define FLASH_PAGE_CNT FLASH_MEM_SIZE / FLASH_PAGE_SIZE_BYTES
+
+BITARRAY_ALLOC(page_dirty, FLASH_PAGE_CNT);
+static BitArray lpc2_bitx;
+
+uint8_t erase_group[] = {
+
+ 4096 / FLASH_PAGE_SIZE_BYTES, 4096 / FLASH_PAGE_SIZE_BYTES,
+ 4096 / FLASH_PAGE_SIZE_BYTES, 4096 / FLASH_PAGE_SIZE_BYTES,
+
+ 4096 / FLASH_PAGE_SIZE_BYTES, 4096 / FLASH_PAGE_SIZE_BYTES,
+ 4096 / FLASH_PAGE_SIZE_BYTES, 4096 / FLASH_PAGE_SIZE_BYTES,
+
+ 32768 / FLASH_PAGE_SIZE_BYTES, 32768 / FLASH_PAGE_SIZE_BYTES,
+ 32768 / FLASH_PAGE_SIZE_BYTES, 32768 / FLASH_PAGE_SIZE_BYTES,
+
+ 32768 / FLASH_PAGE_SIZE_BYTES, 32768 / FLASH_PAGE_SIZE_BYTES,
+ 32768 / FLASH_PAGE_SIZE_BYTES, 32768 / FLASH_PAGE_SIZE_BYTES,
+
+ 32768 / FLASH_PAGE_SIZE_BYTES, 32768 / FLASH_PAGE_SIZE_BYTES,
+ 32768 / FLASH_PAGE_SIZE_BYTES, 32768 / FLASH_PAGE_SIZE_BYTES,
+
+ 32768 / FLASH_PAGE_SIZE_BYTES, 32768 / FLASH_PAGE_SIZE_BYTES,
+ 4096 / FLASH_PAGE_SIZE_BYTES, 4096 / FLASH_PAGE_SIZE_BYTES,
+
+ 4096 / FLASH_PAGE_SIZE_BYTES, 4096 / FLASH_PAGE_SIZE_BYTES,
+ 4096 / FLASH_PAGE_SIZE_BYTES, 4096 / FLASH_PAGE_SIZE_BYTES,
+};
+
typedef struct IapCmd
{
uint32_t cmd;
typedef void (*iap_callback_t)(IapCmd *, IapRes *);
-iap_callback_t iap=(iap_callback_t)IAP_ADDRESS;
-
-#define FLASH_MEM_SIZE (504 * 1024L)
-
-static size_t flash_start_addr;
+iap_callback_t iap = (iap_callback_t)IAP_ADDRESS;
-static size_t sector_size(page_t page)
+static size_t sector_size(uint32_t page)
{
if (page < 8)
return 4096;
return 0;
}
-static size_t sector_addr(page_t page)
+static size_t sector_addr(uint32_t page)
{
if (page < 8)
return page * 4096;
}
-static page_t addr_to_sector(size_t addr)
+static uint32_t addr_to_sector(size_t addr)
{
if (addr < 4096 * 8)
return addr / 4096;
return 0;
}
-static page_addr_t addr_to_pageaddr(size_t addr)
+static size_t lpc2_flash_readDirect(struct KBlock *blk, block_idx_t idx, void *buf, size_t offset, size_t size)
{
- if (addr < 4096 * 8)
- return addr % 4096;
- else if (addr < 4096 * 8 + 32768L * 14)
- return (addr - 4096 * 8) % 32768;
- else if (addr < 4096 * 8 + 32768L * 14 + 4096 * 6)
- return (addr - 4096 * 8 - 32768L * 14) % 4096;
-
- ASSERT(0);
- return 0;
+ memcpy(buf, (void *)(idx * blk->blk_size + offset), size);
+ return size;
}
+static size_t lpc2_flash_writeDirect(struct KBlock *blk, block_idx_t idx, const void *_buf, size_t offset, size_t size)
+{
+ ASSERT(offset == 0);
+ ASSERT(FLASH_PAGE_SIZE_BYTES == size);
-static uint32_t page_buf[1024];//MAX_FLASH_PAGE_SIZE / sizeof(uint32_t)];
-static bool page_dirty;
-static page_t curr_page;
+ Flash *fls = FLASH_CAST(blk);
+ if (!(fls->hw->flags & FLASH_WRITE_ONCE))
+ ASSERT(sector_size(idx) <= FLASH_PAGE_SIZE_BYTES);
-/**
- * Send write command.
- *
- * After WR command cpu write bufferd page into flash memory.
- *
- */
-static void flash_lpc2_writePage(page_t page, uint32_t *buf)
-{
+ const uint8_t *buf = (const uint8_t *)_buf;
cpu_flags_t flags;
//Compute page address of current page.
- page_addr_t addr = sector_addr(page);
-
- LOG_INFO("Writing page %ld...\n", page);
+ uint32_t addr = idx * blk->blk_size;
+ uint32_t sector = addr_to_sector(addr);
+ // Compute the first page index in the sector to manage the status
+ int idx_sector = sector_addr(sector) / blk->blk_size;
+ LOG_INFO("Writing page[%ld]sector[%ld]idx[%d]\n", idx, sector, idx_sector);
IRQ_SAVE_DISABLE(flags);
IapCmd cmd;
IapRes res;
cmd.cmd = PREPARE_SECTOR_FOR_WRITE;
- cmd.param[0] = cmd.param[1] = page;
+ cmd.param[0] = cmd.param[1] = sector;
iap(&cmd, &res);
- if (res.status != CMD_SUCCESS)
- LOG_ERR("%ld\n", res.status);
- cmd.cmd = ERASE_SECTOR;
- cmd.param[0] = cmd.param[1] = page;
- cmd.param[2] = CPU_FREQ / 1000;
- iap(&cmd, &res);
if (res.status != CMD_SUCCESS)
- LOG_ERR("%ld\n", res.status);
+ goto flash_error;
- size_t size = sector_size(page);
+ if ((fls->hw->flags & FLASH_WRITE_ONCE) &&
+ bitarray_isRangeFull(&lpc2_bitx, idx_sector, erase_group[sector]))
+ {
+ kputs("blocchi pieni\n");
+ ASSERT(0);
+ goto flash_error;
+ }
+
+ bool erase = false;
+ if ((fls->hw->flags & FLASH_WRITE_ONCE) &&
+ bitarray_isRangeEmpty(&lpc2_bitx, idx_sector, erase_group[sector]))
+ erase = true;
- while (size)
+ if (!(fls->hw->flags & FLASH_WRITE_ONCE))
+ erase = true;
+
+ if (erase)
{
- LOG_INFO("Writing page %ld, addr %ld, size %d\n", page, addr, size);
- cmd.cmd = PREPARE_SECTOR_FOR_WRITE;
- cmd.param[0] = cmd.param[1] = page;
+ cmd.cmd = ERASE_SECTOR;
+ cmd.param[0] = cmd.param[1] = sector;
+ cmd.param[2] = CPU_FREQ / 1000;
iap(&cmd, &res);
- if (res.status != CMD_SUCCESS)
- LOG_ERR("%ld\n", res.status);
- cmd.cmd = COPY_RAM_TO_FLASH;
- cmd.param[0] = addr;
- cmd.param[1] = (uint32_t)buf;
- cmd.param[2] = 4096;
- cmd.param[3] = CPU_FREQ / 1000;
- iap(&cmd, &res);
if (res.status != CMD_SUCCESS)
- LOG_ERR("%ld\n", res.status);
+ goto flash_error;
+ }
- size -= 4096;
- addr += 4096;
- buf += 4096 / sizeof(uint32_t);
+ LOG_INFO("Writing page [%ld], addr [%ld] in sector[%ld]\n", idx, addr, sector);
+ cmd.cmd = PREPARE_SECTOR_FOR_WRITE;
+ cmd.param[0] = cmd.param[1] = sector;
+ iap(&cmd, &res);
+
+ if (res.status != CMD_SUCCESS)
+ goto flash_error;
+
+ if (fls->hw->flags & FLASH_WRITE_ONCE)
+ {
+ if (bitarray_test(&lpc2_bitx, idx))
+ {
+ ASSERT(0);
+ goto flash_error;
+ }
+ else
+ bitarray_set(&lpc2_bitx, idx);
}
+ cmd.cmd = COPY_RAM_TO_FLASH;
+ cmd.param[0] = addr;
+ cmd.param[1] = (uint32_t)buf;
+ cmd.param[2] = FLASH_PAGE_SIZE_BYTES;
+ cmd.param[3] = CPU_FREQ / 1000;
+ iap(&cmd, &res);
+
+ if (res.status != CMD_SUCCESS)
+ goto flash_error;
+
IRQ_RESTORE(flags);
LOG_INFO("Done\n");
+
+ return blk->blk_size;
+
+flash_error:
+ IRQ_RESTORE(flags);
+ LOG_ERR("%ld\n", res.status);
+ fls->hw->status |= FLASH_WR_ERR;
+ return 0;
}
-/**
- * Write modified page on internal latch, and then send write command to
- * flush page to internal flash.
- */
-static int flash_lpc2_flush(UNUSED_ARG(KFile *, _fd))
+static int lpc2_flash_close(UNUSED_ARG(struct KBlock, *blk))
{
- if (page_dirty)
- {
- flash_lpc2_writePage(curr_page, page_buf);
- page_dirty = false;
- }
+ memset(page_dirty, 0, sizeof(page_dirty));
return 0;
}
-/**
- * Check current page and if \a page is different, load it in
- * temporary buffer.
- */
-static void flash_lpc2_loadPage(KFile *fd, page_t page)
+static int lpc2_flash_error(struct KBlock *blk)
{
- if (page != curr_page)
- {
- flash_lpc2_flush(fd);
-
- size_t addr = sector_addr(page);
- size_t size = sector_size(page);
- LOG_INFO("page %ld, addr %d, size %d\n", page, addr, size);
- // Load page
- memcpy(page_buf, (char *)addr, size);
- curr_page = page;
- LOG_INFO("Loaded page %lu\n", curr_page);
- }
+ Flash *fls = FLASH_CAST(blk);
+ return fls->hw->status;
}
-/**
- * Write program memory.
- * Write \a size bytes from buffer \a _buf to file \a fd
- * \note Write operations are buffered.
- */
-static size_t flash_lpc2_write(struct KFile *fd, const void *_buf, size_t size)
+static void lpc2_flash_clearerror(struct KBlock *blk)
{
- const uint8_t *buf =(const uint8_t *)_buf;
-
- page_t page;
- page_addr_t page_addr;
- size_t total_write = 0;
-
- size = MIN((kfile_off_t)size, fd->size - fd->seek_pos);
-
- LOG_INFO("Writing at pos[%lu]\n", fd->seek_pos);
- while (size)
- {
- page = addr_to_sector(fd->seek_pos + flash_start_addr);
- page_addr = addr_to_pageaddr(fd->seek_pos + flash_start_addr);
- LOG_INFO("addr %ld, page %ld, page_addr %ld\n", fd->seek_pos + flash_start_addr, page, page_addr);
+ Flash *fls = FLASH_CAST(blk);
+ fls->hw->status = 0;
+}
- flash_lpc2_loadPage(fd, page);
+static const KBlockVTable flash_lpc2_buffered_vt =
+{
+ .readDirect = lpc2_flash_readDirect,
+ .writeDirect = lpc2_flash_writeDirect,
- size_t wr_len = MIN(size, (size_t)(sector_size(page) - page_addr));
+ .readBuf = kblock_swReadBuf,
+ .writeBuf = kblock_swWriteBuf,
+ .load = kblock_swLoad,
+ .store = kblock_swStore,
- memcpy(((uint8_t *)page_buf) + page_addr, buf, wr_len);
- page_dirty = true;
+ .close = lpc2_flash_close,
- buf += wr_len;
- fd->seek_pos += wr_len;
- size -= wr_len;
- total_write += wr_len;
- }
- LOG_INFO("written %u bytes\n", total_write);
- return total_write;
-}
+ .error = lpc2_flash_error,
+ .clearerr = lpc2_flash_clearerror,
+};
-/**
- * Open flash file \a fd
- * \a name and \a mode are unused, cause flash memory is
- * threated like one file.
- */
-static void flash_lpc2_open(struct FlashLpc2 *fd)
+static const KBlockVTable flash_lpc2_unbuffered_vt =
{
- curr_page = addr_to_sector(FLASH_BOOT_SIZE);
- if (addr_to_pageaddr(FLASH_BOOT_SIZE))
- curr_page++;
+ .readDirect = lpc2_flash_readDirect,
+ .writeDirect = lpc2_flash_writeDirect,
- flash_start_addr = sector_addr(curr_page);
- LOG_INFO("Boot size %d, curr_page %ld, flash_start_addr %d\n", FLASH_BOOT_SIZE, curr_page, flash_start_addr);
+ .close = lpc2_flash_close,
- fd->fd.size = FLASH_MEM_SIZE - sector_addr(curr_page);
- fd->fd.seek_pos = 0;
+ .error = lpc2_flash_error,
+ .clearerr = lpc2_flash_clearerror,
+};
- memcpy(page_buf, (char *)sector_addr(curr_page), sector_size(curr_page));
+static struct FlashHardware flash_lpc2_hw;
+static uint8_t flash_buf[FLASH_PAGE_SIZE_BYTES];
- page_dirty = false;
- LOG_INFO("Flash file opened, pos %ld, size %ld\n", fd->fd.seek_pos, fd->fd.size);
-}
-
-/**
- * Read from file \a fd \a size bytes and put it in buffer \a buf
- * \return the number of bytes read.
- */
-static size_t flash_lpc2_read(struct KFile *fd, void *_buf, size_t size)
+static void common_init(Flash *fls, int flags)
{
- uint8_t *buf =(uint8_t *)_buf;
-
- size = MIN((kfile_off_t)size, fd->size - fd->seek_pos);
-
- LOG_INFO("Reading at pos[%lu]\n", fd->seek_pos);
+ memset(fls, 0, sizeof(*fls));
+ DB(fls->blk.priv.type = KBT_FLASH);
- // Flush current buffered page (if modified).
- flash_lpc2_flush(fd);
+ fls->hw = &flash_lpc2_hw;
+ fls->hw->flags = flags;
- kfile_off_t *addr = (kfile_off_t *)(fd->seek_pos + flash_start_addr);
- LOG_INFO("actual addr %ld\n", (uint32_t)addr);
- memcpy(buf, addr, size);
+ fls->blk.blk_size = FLASH_PAGE_SIZE_BYTES;
+ fls->blk.blk_cnt = FLASH_MEM_SIZE / FLASH_PAGE_SIZE_BYTES;
- for (unsigned i = 0; i< size; i++)
- {
- if (i % 16 == 0)
- kputchar('\n');
+ bitarray_init(&lpc2_bitx, FLASH_PAGE_CNT, page_dirty, sizeof(page_dirty));
+}
- kprintf("%02x ", buf[i]);
- }
- kputchar('\n');
+void flash_hw_init(Flash *fls, int flags)
+{
+ common_init(fls, flags);
+ fls->blk.priv.vt = &flash_lpc2_buffered_vt;
+ fls->blk.priv.flags |= KB_BUFFERED | KB_PARTIAL_WRITE;
+ fls->blk.priv.buf = flash_buf;
- fd->seek_pos += size;
- LOG_INFO("Read %u bytes\n", size);
- return size;
+ /* Load the first block in the cache */
+ void *flash_start = 0x0;
+ memcpy(fls->blk.priv.buf, flash_start, fls->blk.blk_size);
}
-
-/**
- * Init module to perform write and read operation on internal
- * flash memory.
- */
-void flash_hw_init(struct FlashLpc2 *fd)
+void flash_hw_initUnbuffered(Flash *fls, int flags)
{
- memset(fd, 0, sizeof(*fd));
- // Init base class.
- kfile_init(&fd->fd);
- DB(fd->fd._type = KFT_FLASH);
-
- // Set up flash programming functions.
- fd->fd.write = flash_lpc2_write;
- fd->fd.read = flash_lpc2_read;
- fd->fd.flush = flash_lpc2_flush;
-
- flash_lpc2_open(fd);
- TRACE;
+ common_init(fls, flags);
+ fls->blk.priv.vt = &flash_lpc2_unbuffered_vt;
}