Move kfile interface to the io/ directory.
[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 "flash_lm3s.h"
39 #include "cfg/log.h"
40
41 #include <cfg/macros.h>
42
43 #include <io/kfile.h>
44
45 #include <drv/timer.h>
46 #include <drv/flash.h>
47 #include <cpu/power.h> /* cpu_relax() */
48
49 #include <string.h> /* memcpy() */
50
51
52 static int flash_lm3s_erase_page(page_t addr)
53 {
54         FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
55
56         FLASH_FMA_R = (volatile uint32_t)addr;
57         FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_ERASE;
58
59         while (FLASH_FMC_R & FLASH_FMC_ERASE)
60                 cpu_relax();
61         if (FLASH_FCRIS_R & FLASH_FCRIS_ARIS)
62                 return -1;
63         return 0;
64 }
65
66 static int flash_lm3s_write_word(page_t addr, const uint8_t *data, size_t len)
67 {
68         FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
69
70         uint32_t _data;
71         memcpy(&_data, data, len);
72         FLASH_FMA_R = (volatile uint32_t)addr;
73         FLASH_FMD_R = (volatile uint32_t)_data;
74         FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_WRITE;
75
76         while (FLASH_FMC_R & FLASH_FMC_WRITE)
77                 cpu_relax();
78         if (FLASH_FCRIS_R & FLASH_FCRIS_ARIS)
79                 return -1;
80         return 0;
81 }
82
83 static void _flash_lm3s_flush(Flash *fd)
84 {
85         if (!fd->page_dirty)
86                 return;
87
88         LOG_INFO("Erase page %p\n", fd->curr_page);
89         flash_lm3s_erase_page(fd->curr_page);
90
91         LOG_INFO("Flush page %p\n", fd->curr_page);
92         for (int i = 0; i < FLASH_PAGE_SIZE_BYTES; i+=4)
93                 flash_lm3s_write_word(fd->curr_page + i, &fd->page_buf[i], sizeof(uint32_t));
94         fd->page_dirty = false;
95 }
96
97 static void flash_lm3s_load_page(Flash *fd, page_t page)
98 {
99         ASSERT(!((size_t)page % FLASH_PAGE_SIZE_BYTES));
100
101         if (page == fd->curr_page)
102                 return;
103
104         /* Flush old page */
105         _flash_lm3s_flush(fd);
106
107         /* Load a new page */
108         memcpy(fd->page_buf, FLASH_BASE + (uint8_t *)page, FLASH_PAGE_SIZE_BYTES);
109         fd->curr_page = page;
110         LOG_INFO("Loaded page %p\n", fd->curr_page);
111 }
112
113 /**
114  * Write program memory.
115  * Write \a size bytes from buffer \a _buf to file \a fd
116  * \note Write operations are not buffered.
117  */
118 static size_t flash_lm3s_write(struct KFile *_fd, const void *_buf, size_t size)
119 {
120         Flash *fd = FLASH_CAST(_fd);
121         const uint8_t *buf =(const uint8_t *)_buf;
122         size_t total_write = 0;
123         size_t len;
124
125         size = MIN((kfile_off_t)size,
126                 (kfile_off_t)(fd->fd.size - (fd->fd.seek_pos - FLASH_BASE)));
127
128         LOG_INFO("Writing at pos[%lx]\n", fd->fd.seek_pos);
129         while (size)
130         {
131                 page_t page = (fd->fd.seek_pos & ~(FLASH_PAGE_SIZE_BYTES - 1));
132                 size_t offset = fd->fd.seek_pos % FLASH_PAGE_SIZE_BYTES;
133
134                 flash_lm3s_load_page(fd, page);
135
136                 len = MIN(size, FLASH_PAGE_SIZE_BYTES - offset);
137
138                 memcpy((uint8_t *)fd->page_buf + offset, buf, len);
139                 fd->page_dirty = true;
140
141                 buf += len;
142                 fd->fd.seek_pos += len;
143                 size -= len;
144                 total_write += len;
145         }
146         LOG_INFO("written %u bytes\n", total_write);
147         return total_write;
148 }
149
150 /**
151  * Close file \a fd
152  */
153 static int flash_lm3s_close(struct KFile *_fd)
154 {
155         Flash *fd = FLASH_CAST(_fd);
156         _flash_lm3s_flush(fd);
157         LOG_INFO("Flash file closed\n");
158         return 0;
159 }
160
161 /**
162  * Open flash file \a fd
163  */
164 static void flash_lm3s_open(Flash *fd)
165 {
166         fd->fd.size = FLASH_BASE + FLASH_MEM_SIZE;
167         fd->fd.seek_pos = FLASH_BASE;
168         /*
169          * Set an invalid page to force the load of the next actually used page
170          * in cache.
171          */
172         fd->curr_page = FLASH_BASE + FLASH_MEM_SIZE;
173
174         fd->page_dirty = false;
175         LOG_INFO("Flash file opened\n");
176 }
177
178 /**
179  * Move \a fd file seek position of \a offset bytes from \a whence.
180  */
181 static kfile_off_t flash_lm3s_seek(struct KFile *_fd, kfile_off_t offset, KSeekMode whence)
182 {
183         Flash *fd = FLASH_CAST(_fd);
184         kfile_off_t seek_pos;
185
186         switch (whence)
187         {
188         case KSM_SEEK_SET:
189                 seek_pos = FLASH_BASE;
190                 break;
191         case KSM_SEEK_END:
192                 seek_pos = FLASH_BASE + fd->fd.size;
193                 break;
194         case KSM_SEEK_CUR:
195                 seek_pos = fd->fd.seek_pos;
196                 break;
197         default:
198                 ASSERT(0);
199                 return EOF;
200                 break;
201         }
202         if (seek_pos + offset > fd->fd.size)
203                 LOG_ERR("seek outside EOF\n");
204         fd->fd.seek_pos = seek_pos + offset;
205
206         return fd->fd.seek_pos - FLASH_BASE;
207 }
208
209 /**
210  * Reopen file \a fd
211  */
212 static struct KFile *flash_lm3s_reopen(struct KFile *_fd)
213 {
214         Flash *fd = FLASH_CAST(_fd);
215         flash_lm3s_close(_fd);
216         flash_lm3s_open(fd);
217
218         return _fd;
219 }
220
221 /**
222  * Read from file \a fd \a size bytes and put it in buffer \a buf
223  * \return the number of bytes read.
224  */
225 static size_t flash_lm3s_read(struct KFile *_fd, void *_buf, size_t size)
226 {
227         Flash *fd = FLASH_CAST(_fd);
228         uint8_t *buf =(uint8_t *)_buf, *addr;
229
230         size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
231
232         LOG_INFO("Reading at pos[%lx]\n", fd->fd.seek_pos);
233         /* Check if we can get current cached page */
234         if ((size_t)fd->fd.seek_pos / FLASH_PAGE_SIZE_BYTES ==
235                                 (size_t)fd->curr_page)
236                 addr = (uint8_t *)fd->curr_page +
237                         fd->fd.seek_pos % FLASH_PAGE_SIZE_BYTES;
238         else
239                 addr = (uint8_t *)fd->fd.seek_pos;
240         memcpy(buf, (uint8_t *)addr, size);
241         fd->fd.seek_pos += size;
242
243         LOG_INFO("Read %u bytes\n", size);
244         return size;
245 }
246
247 static int flash_lm3s_flush(struct KFile *_fd)
248 {
249         Flash *fd = FLASH_CAST(_fd);
250
251         _flash_lm3s_flush(fd);
252         return 0;
253 }
254
255 /**
256  * Init module to perform write and read operation on internal
257  * flash memory.
258  */
259 void flash_hw_init(Flash *fd)
260 {
261         memset(fd, 0, sizeof(*fd));
262         DB(fd->fd._type = KFT_FLASH);
263
264         fd->fd.reopen = flash_lm3s_reopen;
265         fd->fd.close = flash_lm3s_close;
266         fd->fd.write = flash_lm3s_write;
267         fd->fd.read = flash_lm3s_read;
268         fd->fd.seek = flash_lm3s_seek;
269         fd->fd.flush = flash_lm3s_flush;
270
271         FLASH_USECRL_R = CPU_FREQ / 1000000 - 1;
272
273         flash_lm3s_open(fd);
274 }