4 * This file is part of BeRTOS.
6 * Bertos is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
33 * \brief LM3S1968 internal flash memory driver.
35 * \author Andrea Righi <arighi@develer.com>
38 #include <cfg/macros.h>
39 #include <kern/kfile.h>
40 #include <drv/timer.h>
41 #include <cpu/power.h> /* cpu_relax() */
42 #include <string.h> /* memcpy() */
44 #include "flash_lm3s.h"
46 static int flash_lm3s_erase_page(volatile uint32_t *addr)
48 FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
50 FLASH_FMA_R = (uint32_t)addr;
51 FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_ERASE;
53 while (FLASH_FMC_R & FLASH_FMC_ERASE)
55 if (FLASH_FCRIS_R & FLASH_FCRIS_ARIS)
60 static int flash_lm3s_write_word(volatile uint32_t *addr, uint32_t data)
62 FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
64 FLASH_FMA_R = (uint32_t)addr;
66 FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_WRITE;
68 while (FLASH_FMC_R & FLASH_FMC_WRITE)
70 if (FLASH_FCRIS_R & FLASH_FCRIS_ARIS)
75 static void _flash_lm3s_flush(FlashLM3S *fd)
81 LOG_INFO("Erase page %p\n", fd->curr_page);
82 flash_lm3s_erase_page(fd->curr_page);
83 LOG_INFO("Flush page %p\n", fd->curr_page);
84 for (i = 0; i < FLASH_PAGE_SIZE_BYTES / sizeof(uint32_t); i++)
85 flash_lm3s_write_word(&fd->curr_page[i], fd->page_buf[i]);
86 fd->page_dirty = false;
89 static void flash_lm3s_load_page(FlashLM3S *fd, uint32_t *page)
91 ASSERT(!((size_t)page % FLASH_PAGE_SIZE_BYTES));
93 if (page == fd->curr_page)
97 _flash_lm3s_flush(fd);
100 memcpy(fd->page_buf, FLASH_BASE + (uint8_t *)page, FLASH_PAGE_SIZE_BYTES);
101 fd->curr_page = page;
102 LOG_INFO("Loaded page %p\n", fd->curr_page);
106 * Write program memory.
107 * Write \a size bytes from buffer \a _buf to file \a fd
108 * \note Write operations are not buffered.
110 static size_t flash_lm3s_write(struct KFile *_fd, const void *_buf, size_t size)
112 FlashLM3S *fd = FLASHLM3S_CAST(_fd);
113 const uint8_t *buf =(const uint8_t *)_buf;
114 size_t total_write = 0;
117 size = MIN((kfile_off_t)size,
118 (kfile_off_t)(fd->fd.size - (fd->fd.seek_pos - FLASH_BASE)));
120 LOG_INFO("Writing at pos[%lx]\n", fd->fd.seek_pos);
123 uint32_t *page = (uint32_t *)(fd->fd.seek_pos &
124 ~(FLASH_PAGE_SIZE_BYTES - 1));
125 size_t offset = fd->fd.seek_pos % FLASH_PAGE_SIZE_BYTES;
127 flash_lm3s_load_page(fd, page);
129 len = MIN(size, FLASH_PAGE_SIZE_BYTES - offset);
131 memcpy((uint8_t *)fd->page_buf + offset, buf, len);
132 fd->page_dirty = true;
135 fd->fd.seek_pos += len;
139 LOG_INFO("written %u bytes\n", total_write);
146 static int flash_lm3s_close(struct KFile *_fd)
148 FlashLM3S *fd = FLASHLM3S_CAST(_fd);
149 _flash_lm3s_flush(fd);
150 LOG_INFO("Flash file closed\n");
155 * Open flash file \a fd
157 static void flash_lm3s_open(struct FlashLM3S *fd)
159 fd->fd.size = FLASH_BASE + FLASH_MEM_SIZE;
160 fd->fd.seek_pos = FLASH_BASE;
162 * Set an invalid page to force the load of the next actually used page
165 fd->curr_page = (uint32_t *)FLASH_BASE + FLASH_MEM_SIZE;
167 fd->page_dirty = false;
168 LOG_INFO("Flash file opened\n");
172 * Move \a fd file seek position of \a offset bytes from \a whence.
174 static kfile_off_t flash_lm3s_seek(struct KFile *_fd, kfile_off_t offset, KSeekMode whence)
176 FlashLM3S *fd = FLASHLM3S_CAST(_fd);
177 kfile_off_t seek_pos;
182 seek_pos = FLASH_BASE;
185 seek_pos = fd->fd.size;
188 seek_pos = fd->fd.seek_pos;
195 if (seek_pos + offset > fd->fd.size)
196 LOG_ERR("seek outside EOF\n");
197 fd->fd.seek_pos = seek_pos + offset;
199 return fd->fd.seek_pos - FLASH_BASE;
205 static struct KFile *flash_lm3s_reopen(struct KFile *_fd)
207 FlashLM3S *fd = FLASHLM3S_CAST(_fd);
208 flash_lm3s_close(_fd);
215 * Read from file \a fd \a size bytes and put it in buffer \a buf
216 * \return the number of bytes read.
218 static size_t flash_lm3s_read(struct KFile *_fd, void *_buf, size_t size)
220 FlashLM3S *fd = FLASHLM3S_CAST(_fd);
221 uint8_t *buf =(uint8_t *)_buf, *addr;
223 size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
225 LOG_INFO("Reading at pos[%lx]\n", fd->fd.seek_pos);
226 /* Check if we can get current cached page */
227 if ((size_t)fd->fd.seek_pos / FLASH_PAGE_SIZE_BYTES ==
228 (size_t)fd->curr_page)
229 addr = (uint8_t *)fd->curr_page +
230 fd->fd.seek_pos % FLASH_PAGE_SIZE_BYTES;
232 addr = (uint8_t *)fd->fd.seek_pos;
233 memcpy(buf, (uint8_t *)addr, size);
234 fd->fd.seek_pos += size;
236 LOG_INFO("Read %u bytes\n", size);
240 static int flash_lm3s_flush(struct KFile *_fd)
242 FlashLM3S *fd = FLASHLM3S_CAST(_fd);
244 _flash_lm3s_flush(fd);
249 * Init module to perform write and read operation on internal
252 void flash_lm3sInit(FlashLM3S *fd)
254 memset(fd, 0, sizeof(*fd));
255 DB(fd->fd._type = KFT_FLASHLM3S);
257 fd->fd.reopen = flash_lm3s_reopen;
258 fd->fd.close = flash_lm3s_close;
259 fd->fd.write = flash_lm3s_write;
260 fd->fd.read = flash_lm3s_read;
261 fd->fd.seek = flash_lm3s_seek;
262 fd->fd.flush = flash_lm3s_flush;
264 FLASH_USECRL_R = CPU_FREQ / 1000000 - 1;