From: asterix Date: Wed, 3 Mar 2010 15:00:10 +0000 (+0000) Subject: Add at91sam7 flash driver. X-Git-Tag: 2.4.0~54 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=507e95236d43b2aa6d6b6e46bde5cb92345ab034;hp=9a9d8e33e942f8a2af0828181203c4a1def62adb;p=bertos.git Add at91sam7 flash driver. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@3180 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/cpu/arm/drv/flash_at91.c b/bertos/cpu/arm/drv/flash_at91.c new file mode 100644 index 00000000..778cb11a --- /dev/null +++ b/bertos/cpu/arm/drv/flash_at91.c @@ -0,0 +1,377 @@ +/** + * \file + * + * + * \author Daniele Basile + * + * \brief At91sam7 Internal flash read/write driver. + * + * + */ + +#include "flash_at91.h" + +#include "cfg/cfg_flash_at91.h" +#include + +#include "hw/hw_boot.h" + +// Define log settings for cfg/log.h +#define LOG_LEVEL CONFIG_FLASH_AT91_LOG_LEVEL +#define LOG_FORMAT CONFIG_FLASH_AT91_LOG_FORMAT +#include + + +#include +#include +#include + +#include + +#include + +#include + +#include + +/* + * Check if flash memory is ready to accept other commands. + */ +RAM_FUNC static bool flash_at91_isReady(void) +{ + return (MC_FSR & BV(MC_FRDY)); +} + +/** + * Send write command. + * + * After WR command cpu write bufferd page into flash memory. + */ +RAM_FUNC static void flash_at91_sendWRcmd(uint32_t page) +{ + cpu_flags_t flags; + + // Wait for end of command + while(!flash_at91_isReady()) + { + cpu_relax(); + } + + IRQ_SAVE_DISABLE(flags); + + // Send the 'write page' command + MC_FCR = MC_KEY | MC_FCMD_WP | (MC_PAGEN_MASK & (page << 8)); + + // Wait for end of command + while(!flash_at91_isReady()) + { + cpu_relax(); + } + + IRQ_RESTORE(flags); +} + +/** + * Return 0 if no error are occurred after flash memory + * read or write operation, otherwise return error code. + */ +RAM_FUNC static int flash_at91_getStatus(struct KFile *_fd) +{ + (void)_fd; + + + /* + * This bit is set to one if we programming of at least one locked lock + * region. + */ + if(MC_FSR & BV(MC_LOCKE)) + return -1; + + /* + * This bit is set to one if an invalid command and/or a bad keywords was/were + * written in the Flash Command Register. + */ + if(MC_FSR & BV(MC_PROGE)) + return -2; + + return 0; +} + + +/** + * Write modified page on internal latch, and then send write command to + * flush page to internal flash. + */ +RAM_FUNC static void flash_at91_flush(FlashAt91 *fd) +{ + if (fd->page_dirty) + { + //Compute page address of current page. + arm_page_addr_t *addr = (arm_page_addr_t *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE); + + //Copy modified page into internal latch. + for (arm_page_addr_t page_addr = 0; page_addr < FLASH_PAGE_SIZE_BYTES; page_addr += 4) + { + uint32_t data; + memcpy(&data, &fd->page_buf[page_addr], sizeof(data)); + *addr = data; + addr++; + } + + // Send write command to transfer page from latch to internal flash memory. + flash_at91_sendWRcmd(fd->curr_page); + } +} + +/** + * Flush At91 flash function. + * + * Write current buffered page in flash memory (if modified). + * This function erase flash memory page before writing. + */ +static int flash_at91_kfileFlush(struct KFile *_fd) +{ + FlashAt91 *fd = FLASHAT91_CAST(_fd); + flash_at91_flush(fd); + return 0; +} + + +/** + * Check current page and if \a page is different, load it in + * temporary buffer. + */ +static void flash_at91_loadPage(FlashAt91 *fd, arm_page_t page) +{ + if (page != fd->curr_page) + { + flash_at91_flush(fd); + // Load page + memcpy(fd->page_buf, (const char *)((page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES); + fd->curr_page = page; + LOG_INFO("Loaded page %lu\n", fd->curr_page); + } +} + + +/** + * Write program memory. + * Write \a size bytes from buffer \a _buf to file \a fd + * \note Write operations are buffered. + */ +static size_t flash_at91_write(struct KFile *_fd, const void *_buf, size_t size) +{ + FlashAt91 *fd = FLASHAT91_CAST(_fd); + const uint8_t *buf =(const uint8_t *)_buf; + + arm_page_t page; + arm_page_addr_t page_addr; + size_t total_write = 0; + + size = MIN((kfile_off_t)size, (kfile_off_t)(fd->fd.size - (fd->fd.seek_pos - FLASH_BASE))); + + LOG_INFO("Writing at pos[%lu]\n", fd->fd.seek_pos); + while (size) + { + page = (fd->fd.seek_pos - FLASH_BASE) / FLASH_PAGE_SIZE_BYTES; + page_addr = (fd->fd.seek_pos - FLASH_BASE) % FLASH_PAGE_SIZE_BYTES; + + flash_at91_loadPage(fd, page); + + size_t wr_len = MIN(size, (size_t)(FLASH_PAGE_SIZE_BYTES - page_addr)); + + memcpy(fd->page_buf + page_addr, buf, wr_len); + fd->page_dirty = true; + + buf += wr_len; + fd->fd.seek_pos += wr_len; + size -= wr_len; + total_write += wr_len; + } + LOG_INFO("written %u bytes\n", total_write); + return total_write; +} + +/** + * Close file \a fd + */ +static int flash_at91_close(struct KFile *_fd) +{ + FlashAt91 *fd = FLASHAT91_CAST(_fd); + flash_at91_flush(fd); + LOG_INFO("Flash file closed\n"); + + return 0; +} + +/** + * Open flash file \a fd + * \a name and \a mode are unused, cause flash memory is + * threated like one file. + */ +static void flash_at91_open(struct FlashAt91 *fd) +{ + fd->fd.size = FLASH_BASE + FLASH_MEM_SIZE; + fd->fd.seek_pos = FLASH_BASE + FLASH_BOOT_SIZE; + fd->curr_page = (fd->fd.seek_pos - FLASH_BASE) / FLASH_PAGE_SIZE_BYTES; + + memcpy(fd->page_buf, (const char *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES); + + fd->page_dirty = false; + LOG_INFO("Flash file opened\n"); +} + + +/** + * Move \a fd file seek position of \a offset bytes from \a whence. + * + */ +static kfile_off_t flash_at91_seek(struct KFile *_fd, kfile_off_t offset, KSeekMode whence) +{ + FlashAt91 *fd = FLASHAT91_CAST(_fd); + kfile_off_t seek_pos; + + switch (whence) + { + + case KSM_SEEK_SET: + seek_pos = FLASH_BASE + FLASH_BOOT_SIZE; + break; + case KSM_SEEK_END: + seek_pos = fd->fd.size; + break; + case KSM_SEEK_CUR: + seek_pos = fd->fd.seek_pos; + break; + default: + ASSERT(0); + return EOF; + break; + } + + #if LOG_LEVEL >= LOG_LVL_INFO + /* Bound check */ + if (seek_pos + offset > fd->fd.size) + LOG_INFO("seek outside EOF\n"); + #endif + + fd->fd.seek_pos = seek_pos + offset; + + return fd->fd.seek_pos - (FLASH_BASE + FLASH_BOOT_SIZE); +} + +/** + * Reopen file \a fd + */ +static struct KFile *flash_at91_reopen(struct KFile *_fd) +{ + FlashAt91 *fd = FLASHAT91_CAST(_fd); + flash_at91_close(_fd); + flash_at91_open(fd); + + return _fd; +} + +/** + * 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_at91_read(struct KFile *_fd, void *_buf, size_t size) +{ + FlashAt91 *fd = FLASHAT91_CAST(_fd); + uint8_t *buf =(uint8_t *)_buf; + + size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos); + + LOG_INFO("Reading at pos[%lu]\n", fd->fd.seek_pos); + + // Flush current buffered page (if modified). + flash_at91_flush(fd); + + uint32_t *addr = (uint32_t *)fd->fd.seek_pos; + memcpy(buf, (uint8_t *)addr, size); + + fd->fd.seek_pos += size; + + LOG_INFO("Read %u bytes\n", size); + return size; +} + + +/** + * Init module to perform write and read operation on internal + * flash memory. + */ +void flash_at91_init(FlashAt91 *fd) +{ + memset(fd, 0, sizeof(*fd)); + DB(fd->fd._type = KFT_FLASHAT91); + + // Set up flash programming functions. + fd->fd.reopen = flash_at91_reopen; + fd->fd.close = flash_at91_close; + fd->fd.write = flash_at91_write; + fd->fd.read = flash_at91_read; + fd->fd.seek = flash_at91_seek; + fd->fd.error = flash_at91_getStatus; + fd->fd.flush = flash_at91_kfileFlush; + + flash_at91_open(fd); + + uint32_t fmcn; + uint32_t fws = 0; + + + /* + * Compute values to insert into mode register. + */ + + /* main clocks in 1.5uS */ + fmcn = (CPU_FREQ/1000000ul) + (CPU_FREQ/2000000ul) + 1; + + /* hard overclocking */ + if (fmcn > 0xFF) + fmcn = 0xFF; + + /* Only allow fmcn=0 if clock period is > 30 us = 33kHz. */ + if (CPU_FREQ <= 33333ul) + fmcn = 0; + + /* Only allow fws=0 if clock frequency is < 30 MHz. */ + if (CPU_FREQ > 30000000ul) + { + fws = 1; + } + + // Set wait states and number of MCK cycles in 1.5 usecs + MC_FMR = fmcn << 16 | fws << 8; + +} diff --git a/bertos/cpu/arm/drv/flash_at91.h b/bertos/cpu/arm/drv/flash_at91.h new file mode 100644 index 00000000..1dcd9829 --- /dev/null +++ b/bertos/cpu/arm/drv/flash_at91.h @@ -0,0 +1,102 @@ +/** + * \file + * + * + * \author Daniele Basile + * + * \brief At91sam7 Internal flash read/write driver. + * + * + */ + +#ifndef FLASH_AT91_H +#define FLASH_AT91_H + +#include + +#include + +#include + + +/** + * Define data type to manage page and memory address. + */ +typedef uint32_t arm_page_t; +typedef uint32_t arm_page_addr_t; + +/** + * FlashAt91 KFile context structure. + */ +typedef struct FlashAt91 +{ + /** + * File descriptor. + */ + KFile fd; + + /** + * Flag for checking if current page is modified. + */ + bool page_dirty; + + /** + * Current buffered page. + */ + arm_page_t curr_page; + + /** + * Temporary buffer cointaing data block to + * write on flash. + */ + uint8_t page_buf[FLASH_PAGE_SIZE_BYTES]; + + +} FlashAt91; + +/** + * ID for FlashAt91 + */ +#define KFT_FLASHAT91 MAKE_ID('F', 'A', '9', '1') + +/** + * Convert + ASSERT from generic KFile to FlashAt91. + */ +INLINE FlashAt91 * FLASHAT91_CAST(KFile *fd) +{ + ASSERT(fd->_type == KFT_FLASHAT91); + return (FlashAt91 *)fd; +} + + +void flash_at91_init(FlashAt91 *fd); + +#endif diff --git a/bertos/cpu/arm/io/at91sam7.h b/bertos/cpu/arm/io/at91sam7.h index c983f50b..2783307f 100644 --- a/bertos/cpu/arm/io/at91sam7.h +++ b/bertos/cpu/arm/io/at91sam7.h @@ -144,6 +144,17 @@ #endif +#if CPU_ARM_AT91SAM7S256 || CPU_ARM_AT91SAM7X256 + #define FLASH_MEM_SIZE 0x40000UL ///< Internal flash memory size + #define FLASH_PAGE_SIZE_BYTES 256 ///< Size of cpu flash memory page in bytes + #define FLASH_BANKS_NUM 1 ///< Number of flash banks + #define FLASH_SECTORS_NUM 16 ///< Number of flash sector + #define FLASH_PAGE_PER_SECTOR 64 ///< Number of page for sector + +#else + #error Memory size definition for selected ARM CPU +#endif + #include "at91_aic.h" #include "at91_pit.h" #include "at91_pmc.h" diff --git a/bertos/cpu/attr.h b/bertos/cpu/attr.h index 98ed7b2f..c6d83734 100644 --- a/bertos/cpu/attr.h +++ b/bertos/cpu/attr.h @@ -158,6 +158,11 @@ */ #define ISR_FUNC __attribute__((interrupt)) + /* + * Function attribute to move it into ram memory. + */ + #define RAM_FUNC __attribute__((section(".data"))) + #endif /* !__IAR_SYSTEMS_ICC_ */ #elif CPU_PPC