lm3s1968: internal flash memory driver.
[bertos.git] / bertos / cpu / cortex-m3 / drv / flash_lm3s.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
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.
10  *
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.
15  *
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
19  *
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.
28  *
29  * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief LM3S1968 internal flash memory driver.
34  *
35  * \author Andrea Righi <arighi@develer.com>
36  */
37
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() */
43 #include "cfg/log.h"
44 #include "flash_lm3s.h"
45
46 static int flash_lm3s_erase_page(volatile uint32_t *addr)
47 {
48         FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
49
50         FLASH_FMA_R = (uint32_t)addr;
51         FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_ERASE;
52
53         while (FLASH_FMC_R & FLASH_FMC_ERASE)
54                 cpu_relax();
55         if (FLASH_FCRIS_R & FLASH_FCRIS_ARIS)
56                 return -1;
57         return 0;
58 }
59
60 static int flash_lm3s_write_word(volatile uint32_t *addr, uint32_t data)
61 {
62         FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
63
64         FLASH_FMA_R = (uint32_t)addr;
65         FLASH_FMD_R = data;
66         FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_WRITE;
67
68         while (FLASH_FMC_R & FLASH_FMC_WRITE)
69                 cpu_relax();
70         if (FLASH_FCRIS_R & FLASH_FCRIS_ARIS)
71                 return -1;
72         return 0;
73 }
74
75 static void _flash_lm3s_flush(FlashLM3S *fd)
76 {
77         unsigned int i;
78
79         if (!fd->page_dirty)
80                 return;
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;
87 }
88
89 static void flash_lm3s_load_page(FlashLM3S *fd, uint32_t *page)
90 {
91         ASSERT(!((size_t)page % FLASH_PAGE_SIZE_BYTES));
92
93         if (page == fd->curr_page)
94                 return;
95
96         /* Flush old page */
97         _flash_lm3s_flush(fd);
98
99         /* Load a new page */
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);
103 }
104
105 /**
106  * Write program memory.
107  * Write \a size bytes from buffer \a _buf to file \a fd
108  * \note Write operations are not buffered.
109  */
110 static size_t flash_lm3s_write(struct KFile *_fd, const void *_buf, size_t size)
111 {
112         FlashLM3S *fd = FLASHLM3S_CAST(_fd);
113         const uint8_t *buf =(const uint8_t *)_buf;
114         size_t total_write = 0;
115         size_t len;
116
117         size = MIN((kfile_off_t)size,
118                 (kfile_off_t)(fd->fd.size - (fd->fd.seek_pos - FLASH_BASE)));
119
120         LOG_INFO("Writing at pos[%lx]\n", fd->fd.seek_pos);
121         while (size)
122         {
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;
126
127                 flash_lm3s_load_page(fd, page);
128
129                 len = MIN(size, FLASH_PAGE_SIZE_BYTES - offset);
130
131                 memcpy((uint8_t *)fd->page_buf + offset, buf, len);
132                 fd->page_dirty = true;
133
134                 buf += len;
135                 fd->fd.seek_pos += len;
136                 size -= len;
137                 total_write += len;
138         }
139         LOG_INFO("written %u bytes\n", total_write);
140         return total_write;
141 }
142
143 /**
144  * Close file \a fd
145  */
146 static int flash_lm3s_close(struct KFile *_fd)
147 {
148         FlashLM3S *fd = FLASHLM3S_CAST(_fd);
149         _flash_lm3s_flush(fd);
150         LOG_INFO("Flash file closed\n");
151         return 0;
152 }
153
154 /**
155  * Open flash file \a fd
156  */
157 static void flash_lm3s_open(struct FlashLM3S *fd)
158 {
159         fd->fd.size = FLASH_BASE + FLASH_MEM_SIZE;
160         fd->fd.seek_pos = FLASH_BASE;
161         /*
162          * Set an invalid page to force the load of the next actually used page
163          * in cache.
164          */
165         fd->curr_page = (uint32_t *)FLASH_BASE + FLASH_MEM_SIZE;
166
167         fd->page_dirty = false;
168         LOG_INFO("Flash file opened\n");
169 }
170
171 /**
172  * Move \a fd file seek position of \a offset bytes from \a whence.
173  */
174 static kfile_off_t flash_lm3s_seek(struct KFile *_fd, kfile_off_t offset, KSeekMode whence)
175 {
176         FlashLM3S *fd = FLASHLM3S_CAST(_fd);
177         kfile_off_t seek_pos;
178
179         switch (whence)
180         {
181         case KSM_SEEK_SET:
182                 seek_pos = FLASH_BASE;
183                 break;
184         case KSM_SEEK_END:
185                 seek_pos = fd->fd.size;
186                 break;
187         case KSM_SEEK_CUR:
188                 seek_pos = fd->fd.seek_pos;
189                 break;
190         default:
191                 ASSERT(0);
192                 return EOF;
193                 break;
194         }
195         if (seek_pos + offset > fd->fd.size)
196                 LOG_ERR("seek outside EOF\n");
197         fd->fd.seek_pos = seek_pos + offset;
198
199         return fd->fd.seek_pos - FLASH_BASE;
200 }
201
202 /**
203  * Reopen file \a fd
204  */
205 static struct KFile *flash_lm3s_reopen(struct KFile *_fd)
206 {
207         FlashLM3S *fd = FLASHLM3S_CAST(_fd);
208         flash_lm3s_close(_fd);
209         flash_lm3s_open(fd);
210
211         return _fd;
212 }
213
214 /**
215  * Read from file \a fd \a size bytes and put it in buffer \a buf
216  * \return the number of bytes read.
217  */
218 static size_t flash_lm3s_read(struct KFile *_fd, void *_buf, size_t size)
219 {
220         FlashLM3S *fd = FLASHLM3S_CAST(_fd);
221         uint8_t *buf =(uint8_t *)_buf, *addr;
222
223         size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
224
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;
231         else
232                 addr = (uint8_t *)fd->fd.seek_pos;
233         memcpy(buf, (uint8_t *)addr, size);
234         fd->fd.seek_pos += size;
235
236         LOG_INFO("Read %u bytes\n", size);
237         return size;
238 }
239
240 static int flash_lm3s_flush(struct KFile *_fd)
241 {
242         FlashLM3S *fd = FLASHLM3S_CAST(_fd);
243
244         _flash_lm3s_flush(fd);
245         return 0;
246 }
247
248 /**
249  * Init module to perform write and read operation on internal
250  * flash memory.
251  */
252 void flash_lm3sInit(FlashLM3S *fd)
253 {
254         memset(fd, 0, sizeof(*fd));
255         DB(fd->fd._type = KFT_FLASHLM3S);
256
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;
263
264         FLASH_USECRL_R = CPU_FREQ / 1000000 - 1;
265
266         flash_lm3s_open(fd);
267 }