flash_avr.c

Go to the documentation of this file.
00001 
00044 #include "flash_avr.h"
00045 
00046 #include "cfg/cfg_flash_avr.h"
00047 #include <cfg/macros.h> // MIN()
00048 #include <cfg/compiler.h>
00049 #include <cfg/debug.h>
00050 #include <cpu/irq.h>
00051 
00052 // Define log settings for cfg/log.h
00053 #define LOG_LEVEL    CONFIG_FLASH_AVR_LOG_LEVEL
00054 #define LOG_FORMAT   CONFIG_FLASH_AVR_LOG_FORMAT
00055 #include <cfg/log.h>
00056 
00057 #include <drv/wdt.h>
00058 #include <drv/flash.h>
00059 
00060 #include <kern/kfile.h>
00061 
00062 #include <avr/io.h>
00063 #include <avr/boot.h>
00064 #include <avr/pgmspace.h>
00065 
00066 #include <string.h>
00067 
00068 
00072 typedef uint16_t page_addr_t;
00073 
00074 
00083 static void flash_avr_flush(Flash *fd)
00084 {
00085     if (fd->page_dirty)
00086     {
00087 
00088         LOG_INFO("Flushing page %d\n", fd->curr_page);
00089 
00090         // Wait while the SPM instruction is busy.
00091         boot_spm_busy_wait();
00092 
00093         LOG_INFO("Filling temparary page buffer...");
00094 
00095         // Fill the temporary buffer of the AVR
00096         for (page_addr_t page_addr = 0; page_addr < SPM_PAGESIZE; page_addr += 2)
00097         {
00098             uint16_t word = ((uint16_t)fd->page_buf[page_addr + 1] << 8) | fd->page_buf[page_addr];
00099 
00100             ATOMIC(boot_page_fill(page_addr, word));
00101         }
00102         LOG_INFO("Done.\n");
00103 
00104         wdt_reset();
00105 
00106         LOG_INFO("Erasing page, addr %u...", fd->curr_page * SPM_PAGESIZE);
00107 
00108         /* Page erase */
00109         ATOMIC(boot_page_erase(fd->curr_page * SPM_PAGESIZE));
00110 
00111         /* Wait until the memory is erased. */
00112         boot_spm_busy_wait();
00113 
00114         LOG_INFO("Done.\n");
00115         LOG_INFO("Writing page, addr %u...", fd->curr_page * SPM_PAGESIZE);
00116 
00117         /* Store buffer in flash page. */
00118         ATOMIC(boot_page_write(fd->curr_page * SPM_PAGESIZE));
00119         boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
00120 
00121         /*
00122         * Reenable RWW-section again. We need this if we want to jump back
00123         * to the application after bootloading.
00124         */
00125         ATOMIC(boot_rww_enable());
00126 
00127         fd->page_dirty = false;
00128         LOG_INFO("Done.\n");
00129     }
00130 }
00131 
00132 
00139 static int flash_avr_kfileFlush(struct KFile *_fd)
00140 {
00141     Flash *fd = FLASH_CAST(_fd);
00142     flash_avr_flush(fd);
00143     return 0;
00144 }
00145 
00146 
00151 static void flash_avr_loadPage(Flash *fd, page_t page)
00152 {
00153     if (page != fd->curr_page)
00154     {
00155         flash_avr_flush(fd);
00156         // Load page
00157         memcpy_P(fd->page_buf, (const char *)(page * SPM_PAGESIZE), SPM_PAGESIZE);
00158         fd->curr_page = page;
00159         LOG_INFO("Loaded page %d\n", fd->curr_page);
00160     }
00161 }
00162 
00168 static size_t flash_avr_write(struct KFile *_fd, const void *_buf, size_t size)
00169 {
00170     Flash *fd = FLASH_CAST(_fd);
00171     const uint8_t *buf =(const uint8_t *)_buf;
00172 
00173     page_t page;
00174     page_addr_t page_addr;
00175     size_t total_write = 0;
00176 
00177 
00178     ASSERT(fd->fd.seek_pos + (kfile_off_t)size <= (kfile_off_t)fd->fd.size);
00179     size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
00180 
00181     LOG_INFO("Writing at pos[%u]\n", fd->fd.seek_pos);
00182     while (size)
00183     {
00184         page = fd->fd.seek_pos / SPM_PAGESIZE;
00185         page_addr = fd->fd.seek_pos % SPM_PAGESIZE;
00186 
00187         flash_avr_loadPage(fd, page);
00188 
00189         size_t wr_len = MIN(size, SPM_PAGESIZE - page_addr);
00190         memcpy(fd->page_buf + page_addr, buf, wr_len);
00191         fd->page_dirty = true;
00192 
00193         buf += wr_len;
00194         fd->fd.seek_pos += wr_len;
00195         size -= wr_len;
00196         total_write += wr_len;
00197     }
00198     LOG_INFO("written %u bytes\n", total_write);
00199     return total_write;
00200 }
00201 
00207 static void flash_avr_open(struct Flash *fd)
00208 {
00209     fd->curr_page = 0;
00210     memcpy_P(fd->page_buf, (const char *)(fd->curr_page * SPM_PAGESIZE), SPM_PAGESIZE);
00211 
00212     fd->fd.seek_pos = 0;
00213     fd->fd.size = (uint16_t)(FLASHEND - CONFIG_FLASH_AVR_BOOTSIZE + 1);
00214     fd->page_dirty = false;
00215 
00216     LOG_INFO("Flash file opened\n");
00217 }
00218 
00222 static int flash_avr_close(struct KFile *_fd)
00223 {
00224     Flash *fd = FLASH_CAST(_fd);
00225     flash_avr_flush(fd);
00226     LOG_INFO("Flash file closed\n");
00227     return 0;
00228 }
00229 
00233 static struct KFile *flash_avr_reopen(struct KFile *fd)
00234 {
00235     Flash *_fd = FLASH_CAST(fd);
00236     flash_avr_close(fd);
00237     flash_avr_open((struct Flash *)_fd);
00238     return fd;
00239 }
00240 
00241 
00246 static size_t flash_avr_read(struct KFile *_fd, void *buf, size_t size)
00247 {
00248     Flash *fd = FLASH_CAST(_fd);
00249     ASSERT(fd->fd.seek_pos + (kfile_off_t)size <= (kfile_off_t)fd->fd.size);
00250     size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
00251 
00252     LOG_INFO("Reading at pos[%u]\n", fd->fd.seek_pos);
00253     // Flush current buffered page (if modified).
00254     flash_avr_flush(fd);
00255 
00256     /*
00257      * AVR pointers are 16 bits wide, this hack is needed to avoid
00258      * compiler warning, cause fd->seek_pos is a 32bit offset.
00259      */
00260     const uint8_t *pgm_addr = (const uint8_t *)0;
00261     pgm_addr += fd->fd.seek_pos;
00262 
00263     memcpy_P(buf, pgm_addr, size);
00264     fd->fd.seek_pos += size;
00265     LOG_INFO("Read %u bytes\n", size);
00266     return size;
00267 }
00268 
00272 void flash_hw_init(struct Flash *fd)
00273 {
00274     memset(fd, 0, sizeof(*fd));
00275     DB(fd->fd._type = KFT_FLASH);
00276 
00277     // Set up flash programming functions.
00278     fd->fd.reopen = flash_avr_reopen;
00279     fd->fd.close = flash_avr_close;
00280     fd->fd.read = flash_avr_read;
00281     fd->fd.write = flash_avr_write;
00282     fd->fd.seek = kfile_genericSeek;
00283     fd->fd.flush = flash_avr_kfileFlush;
00284     fd->curr_page = 0;
00285 
00286     flash_avr_open(fd);
00287 }
00288 
00289