flash_at91.c

Go to the documentation of this file.
00001 
00040 #include "flash_at91.h"
00041 
00042 #include "cfg/cfg_flash_at91.h"
00043 #include <cfg/macros.h>
00044 
00045 #include "hw/hw_boot.h"
00046 
00047 // Define log settings for cfg/log.h
00048 #define LOG_LEVEL    CONFIG_FLASH_AT91_LOG_LEVEL
00049 #define LOG_FORMAT   CONFIG_FLASH_AT91_LOG_FORMAT
00050 #include <cfg/log.h>
00051 
00052 
00053 #include <cpu/irq.h>
00054 #include <cpu/attr.h>
00055 #include <cpu/power.h>
00056 
00057 #include <kern/kfile.h>
00058 
00059 #include <io/arm.h>
00060 
00061 #include <drv/timer.h>
00062 
00063 #include <string.h>
00064 
00065 /*
00066  * Check if flash memory is ready to accept other commands.
00067  */
00068 RAM_FUNC static bool flash_at91_isReady(void)
00069 {
00070     return (MC_FSR & BV(MC_FRDY));
00071 }
00072 
00078 RAM_FUNC static void flash_at91_sendWRcmd(uint32_t page)
00079 {
00080     cpu_flags_t flags;
00081 
00082     // Wait for end of command
00083     while(!flash_at91_isReady())
00084     {
00085         cpu_relax();
00086     }
00087 
00088     IRQ_SAVE_DISABLE(flags);
00089 
00090     // Send the 'write page' command
00091     MC_FCR = MC_KEY | MC_FCMD_WP | (MC_PAGEN_MASK & (page << 8));
00092 
00093     // Wait for end of command
00094     while(!flash_at91_isReady())
00095     {
00096         cpu_relax();
00097     }
00098 
00099     IRQ_RESTORE(flags);
00100 }
00101 
00106 RAM_FUNC static int flash_at91_getStatus(struct KFile *_fd)
00107 {
00108     (void)_fd;
00109 
00110 
00111     /*
00112      * This bit is set to one if we programming of at least one locked lock
00113      * region.
00114      */
00115     if(MC_FSR & BV(MC_LOCKE))
00116         return -1;
00117 
00118     /*
00119      * This bit is set to one if an invalid command and/or a bad keywords was/were
00120      * written in the Flash Command Register.
00121      */
00122     if(MC_FSR & BV(MC_PROGE))
00123         return -2;
00124 
00125     return 0;
00126 }
00127 
00128 
00133 RAM_FUNC static void flash_at91_flush(FlashAt91 *fd)
00134 {
00135     if (fd->page_dirty)
00136     {
00137         //Compute page address of current page.
00138         arm_page_addr_t *addr = (arm_page_addr_t *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE);
00139 
00140         //Copy modified page into internal latch.
00141         for (arm_page_addr_t page_addr = 0; page_addr < FLASH_PAGE_SIZE_BYTES; page_addr += 4)
00142         {
00143             uint32_t data;
00144             memcpy(&data, &fd->page_buf[page_addr], sizeof(data));
00145             *addr = data;
00146             addr++;
00147         }
00148 
00149         // Send write command to transfer page from latch to internal flash memory.
00150         flash_at91_sendWRcmd(fd->curr_page);
00151     }
00152 }
00153 
00160 static int flash_at91_kfileFlush(struct KFile *_fd)
00161 {
00162     FlashAt91 *fd = FLASHAT91_CAST(_fd);
00163     flash_at91_flush(fd);
00164     return 0;
00165 }
00166 
00167 
00172 static void flash_at91_loadPage(FlashAt91 *fd, arm_page_t page)
00173 {
00174     if (page != fd->curr_page)
00175     {
00176         flash_at91_flush(fd);
00177         // Load page
00178         memcpy(fd->page_buf, (const char *)((page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES);
00179         fd->curr_page = page;
00180         LOG_INFO("Loaded page %lu\n", fd->curr_page);
00181     }
00182 }
00183 
00184 
00190 static size_t flash_at91_write(struct KFile *_fd, const void *_buf, size_t size)
00191 {
00192     FlashAt91 *fd = FLASHAT91_CAST(_fd);
00193     const uint8_t *buf =(const uint8_t *)_buf;
00194 
00195     arm_page_t page;
00196     arm_page_addr_t page_addr;
00197     size_t total_write = 0;
00198 
00199     size = MIN((kfile_off_t)size, (kfile_off_t)(fd->fd.size - (fd->fd.seek_pos - FLASH_BASE)));
00200 
00201     LOG_INFO("Writing at pos[%lu]\n", fd->fd.seek_pos);
00202     while (size)
00203     {
00204         page = (fd->fd.seek_pos - FLASH_BASE) / FLASH_PAGE_SIZE_BYTES;
00205         page_addr = (fd->fd.seek_pos - FLASH_BASE) % FLASH_PAGE_SIZE_BYTES;
00206 
00207         flash_at91_loadPage(fd, page);
00208 
00209         size_t wr_len = MIN(size, (size_t)(FLASH_PAGE_SIZE_BYTES - page_addr));
00210 
00211         memcpy(fd->page_buf + page_addr, buf, wr_len);
00212         fd->page_dirty = true;
00213 
00214         buf += wr_len;
00215         fd->fd.seek_pos += wr_len;
00216         size -= wr_len;
00217         total_write += wr_len;
00218     }
00219     LOG_INFO("written %u bytes\n", total_write);
00220     return total_write;
00221 }
00222 
00226 static int flash_at91_close(struct KFile *_fd)
00227 {
00228     FlashAt91 *fd = FLASHAT91_CAST(_fd);
00229     flash_at91_flush(fd);
00230     LOG_INFO("Flash file closed\n");
00231 
00232     return 0;
00233 }
00234 
00240 static void flash_at91_open(struct FlashAt91 *fd)
00241 {
00242     fd->fd.size = FLASH_BASE + FLASH_MEM_SIZE;
00243     fd->fd.seek_pos = FLASH_BASE + FLASH_BOOT_SIZE;
00244     fd->curr_page = (fd->fd.seek_pos - FLASH_BASE) / FLASH_PAGE_SIZE_BYTES;
00245 
00246     memcpy(fd->page_buf, (const char *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES);
00247 
00248     fd->page_dirty = false;
00249     LOG_INFO("Flash file opened\n");
00250 }
00251 
00252 
00257 static kfile_off_t flash_at91_seek(struct KFile *_fd, kfile_off_t offset, KSeekMode whence)
00258 {
00259     FlashAt91 *fd = FLASHAT91_CAST(_fd);
00260     kfile_off_t seek_pos;
00261 
00262     switch (whence)
00263     {
00264 
00265     case KSM_SEEK_SET:
00266         seek_pos = FLASH_BASE + FLASH_BOOT_SIZE;
00267         break;
00268     case KSM_SEEK_END:
00269         seek_pos = fd->fd.size;
00270         break;
00271     case KSM_SEEK_CUR:
00272         seek_pos = fd->fd.seek_pos;
00273         break;
00274     default:
00275         ASSERT(0);
00276         return EOF;
00277         break;
00278     }
00279 
00280     #if LOG_LEVEL >= LOG_LVL_INFO
00281     /* Bound check */
00282     if (seek_pos + offset > fd->fd.size)
00283         LOG_INFO("seek outside EOF\n");
00284     #endif
00285 
00286     fd->fd.seek_pos = seek_pos + offset;
00287 
00288     return fd->fd.seek_pos - (FLASH_BASE + FLASH_BOOT_SIZE);
00289 }
00290 
00294 static struct KFile *flash_at91_reopen(struct KFile *_fd)
00295 {
00296     FlashAt91 *fd = FLASHAT91_CAST(_fd);
00297     flash_at91_close(_fd);
00298     flash_at91_open(fd);
00299 
00300     return _fd;
00301 }
00302 
00307 static size_t flash_at91_read(struct KFile *_fd, void *_buf, size_t size)
00308 {
00309     FlashAt91 *fd = FLASHAT91_CAST(_fd);
00310     uint8_t *buf =(uint8_t *)_buf;
00311 
00312     size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
00313 
00314     LOG_INFO("Reading at pos[%lu]\n", fd->fd.seek_pos);
00315 
00316     // Flush current buffered page (if modified).
00317     flash_at91_flush(fd);
00318 
00319     uint32_t *addr = (uint32_t *)fd->fd.seek_pos;
00320     memcpy(buf, (uint8_t *)addr, size);
00321 
00322     fd->fd.seek_pos += size;
00323 
00324     LOG_INFO("Read %u bytes\n", size);
00325     return size;
00326 }
00327 
00328 
00333 void flash_at91_init(FlashAt91 *fd)
00334 {
00335     memset(fd, 0, sizeof(*fd));
00336     DB(fd->fd._type = KFT_FLASHAT91);
00337 
00338     // Set up flash programming functions.
00339     fd->fd.reopen = flash_at91_reopen;
00340     fd->fd.close = flash_at91_close;
00341     fd->fd.write = flash_at91_write;
00342     fd->fd.read = flash_at91_read;
00343     fd->fd.seek = flash_at91_seek;
00344     fd->fd.error = flash_at91_getStatus;
00345     fd->fd.flush = flash_at91_kfileFlush;
00346 
00347     flash_at91_open(fd);
00348 
00349     uint32_t fmcn;
00350     uint32_t fws = 0;
00351 
00352 
00353     /*
00354      * Compute values to insert into mode register.
00355      */
00356 
00357     /* main clocks in 1.5uS */
00358     fmcn = (CPU_FREQ/1000000ul) + (CPU_FREQ/2000000ul) + 1;
00359 
00360     /* hard overclocking */
00361     if (fmcn > 0xFF)
00362         fmcn = 0xFF;
00363 
00364     /* Only allow fmcn=0 if clock period is > 30 us = 33kHz. */
00365     if (CPU_FREQ <= 33333ul)
00366         fmcn = 0;
00367 
00368     /* Only allow fws=0 if clock frequency is < 30 MHz. */
00369     if (CPU_FREQ > 30000000ul)
00370     {
00371         fws = 1;
00372     }
00373 
00374     // Set wait states and number of MCK cycles in 1.5 usecs
00375     MC_FMR = fmcn << 16 | fws << 8;
00376 
00377 }