778cb11a1997ce42f40891fb27b537bc4d451df1
[bertos.git] / bertos / cpu / arm / drv / flash_at91.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 2009 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \author Daniele Basile <asterix@develer.com>
34  *
35  * \brief At91sam7 Internal flash read/write driver.
36  *
37  *
38  */
39
40 #include "flash_at91.h"
41
42 #include "cfg/cfg_flash_at91.h"
43 #include <cfg/macros.h>
44
45 #include "hw/hw_boot.h"
46
47 // Define log settings for cfg/log.h
48 #define LOG_LEVEL    CONFIG_FLASH_AT91_LOG_LEVEL
49 #define LOG_FORMAT   CONFIG_FLASH_AT91_LOG_FORMAT
50 #include <cfg/log.h>
51
52
53 #include <cpu/irq.h>
54 #include <cpu/attr.h>
55 #include <cpu/power.h>
56
57 #include <kern/kfile.h>
58
59 #include <io/arm.h>
60
61 #include <drv/timer.h>
62
63 #include <string.h>
64
65 /*
66  * Check if flash memory is ready to accept other commands.
67  */
68 RAM_FUNC static bool flash_at91_isReady(void)
69 {
70         return (MC_FSR & BV(MC_FRDY));
71 }
72
73 /**
74  * Send write command.
75  *
76  * After WR command cpu write bufferd page into flash memory.
77  */
78 RAM_FUNC static void flash_at91_sendWRcmd(uint32_t page)
79 {
80         cpu_flags_t flags;
81
82         // Wait for end of command
83         while(!flash_at91_isReady())
84         {
85                 cpu_relax();
86         }
87
88         IRQ_SAVE_DISABLE(flags);
89
90         // Send the 'write page' command
91         MC_FCR = MC_KEY | MC_FCMD_WP | (MC_PAGEN_MASK & (page << 8));
92
93         // Wait for end of command
94         while(!flash_at91_isReady())
95         {
96                 cpu_relax();
97         }
98
99         IRQ_RESTORE(flags);
100 }
101
102 /**
103  * Return 0 if no error are occurred after flash memory
104  * read or write operation, otherwise return error code.
105  */
106 RAM_FUNC static int flash_at91_getStatus(struct KFile *_fd)
107 {
108         (void)_fd;
109
110
111         /*
112          * This bit is set to one if we programming of at least one locked lock
113          * region.
114          */
115         if(MC_FSR & BV(MC_LOCKE))
116                 return -1;
117
118         /*
119          * This bit is set to one if an invalid command and/or a bad keywords was/were
120          * written in the Flash Command Register.
121          */
122         if(MC_FSR & BV(MC_PROGE))
123                 return -2;
124
125         return 0;
126 }
127
128
129 /**
130  * Write modified page on internal latch, and then send write command to
131  * flush page to internal flash.
132  */
133 RAM_FUNC static void flash_at91_flush(FlashAt91 *fd)
134 {
135         if (fd->page_dirty)
136         {
137                 //Compute page address of current page.
138                 arm_page_addr_t *addr = (arm_page_addr_t *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE);
139
140                 //Copy modified page into internal latch.
141                 for (arm_page_addr_t page_addr = 0; page_addr < FLASH_PAGE_SIZE_BYTES; page_addr += 4)
142                 {
143                         uint32_t data;
144                         memcpy(&data, &fd->page_buf[page_addr], sizeof(data));
145                         *addr = data;
146                         addr++;
147                 }
148
149                 // Send write command to transfer page from latch to internal flash memory.
150                 flash_at91_sendWRcmd(fd->curr_page);
151         }
152 }
153
154 /**
155  * Flush At91 flash function.
156  *
157  * Write current buffered page in flash memory (if modified).
158  * This function erase flash memory page before writing.
159  */
160 static int flash_at91_kfileFlush(struct KFile *_fd)
161 {
162         FlashAt91 *fd = FLASHAT91_CAST(_fd);
163         flash_at91_flush(fd);
164         return 0;
165 }
166
167
168 /**
169  * Check current page and if \a page is different, load it in
170  * temporary buffer.
171  */
172 static void flash_at91_loadPage(FlashAt91 *fd, arm_page_t page)
173 {
174         if (page != fd->curr_page)
175         {
176                 flash_at91_flush(fd);
177                 // Load page
178                 memcpy(fd->page_buf, (const char *)((page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES);
179                 fd->curr_page = page;
180                 LOG_INFO("Loaded page %lu\n", fd->curr_page);
181         }
182 }
183
184
185 /**
186  * Write program memory.
187  * Write \a size bytes from buffer \a _buf to file \a fd
188  * \note Write operations are buffered.
189  */
190 static size_t flash_at91_write(struct KFile *_fd, const void *_buf, size_t size)
191 {
192         FlashAt91 *fd = FLASHAT91_CAST(_fd);
193         const uint8_t *buf =(const uint8_t *)_buf;
194
195         arm_page_t page;
196         arm_page_addr_t page_addr;
197         size_t total_write = 0;
198
199         size = MIN((kfile_off_t)size, (kfile_off_t)(fd->fd.size - (fd->fd.seek_pos - FLASH_BASE)));
200
201         LOG_INFO("Writing at pos[%lu]\n", fd->fd.seek_pos);
202         while (size)
203         {
204                 page = (fd->fd.seek_pos - FLASH_BASE) / FLASH_PAGE_SIZE_BYTES;
205                 page_addr = (fd->fd.seek_pos - FLASH_BASE) % FLASH_PAGE_SIZE_BYTES;
206
207                 flash_at91_loadPage(fd, page);
208
209                 size_t wr_len = MIN(size, (size_t)(FLASH_PAGE_SIZE_BYTES - page_addr));
210
211                 memcpy(fd->page_buf + page_addr, buf, wr_len);
212                 fd->page_dirty = true;
213
214                 buf += wr_len;
215                 fd->fd.seek_pos += wr_len;
216                 size -= wr_len;
217                 total_write += wr_len;
218         }
219         LOG_INFO("written %u bytes\n", total_write);
220         return total_write;
221 }
222
223 /**
224  * Close file \a fd
225  */
226 static int flash_at91_close(struct KFile *_fd)
227 {
228         FlashAt91 *fd = FLASHAT91_CAST(_fd);
229         flash_at91_flush(fd);
230         LOG_INFO("Flash file closed\n");
231
232         return 0;
233 }
234
235 /**
236  * Open flash file \a fd
237  * \a name and \a mode are unused, cause flash memory is
238  * threated like one file.
239  */
240 static void flash_at91_open(struct FlashAt91 *fd)
241 {
242         fd->fd.size = FLASH_BASE + FLASH_MEM_SIZE;
243         fd->fd.seek_pos = FLASH_BASE + FLASH_BOOT_SIZE;
244         fd->curr_page = (fd->fd.seek_pos - FLASH_BASE) / FLASH_PAGE_SIZE_BYTES;
245
246         memcpy(fd->page_buf, (const char *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES);
247
248         fd->page_dirty = false;
249         LOG_INFO("Flash file opened\n");
250 }
251
252
253 /**
254  * Move \a fd file seek position of \a offset bytes from \a whence.
255  *
256  */
257 static kfile_off_t flash_at91_seek(struct KFile *_fd, kfile_off_t offset, KSeekMode whence)
258 {
259         FlashAt91 *fd = FLASHAT91_CAST(_fd);
260         kfile_off_t seek_pos;
261
262         switch (whence)
263         {
264
265         case KSM_SEEK_SET:
266                 seek_pos = FLASH_BASE + FLASH_BOOT_SIZE;
267                 break;
268         case KSM_SEEK_END:
269                 seek_pos = fd->fd.size;
270                 break;
271         case KSM_SEEK_CUR:
272                 seek_pos = fd->fd.seek_pos;
273                 break;
274         default:
275                 ASSERT(0);
276                 return EOF;
277                 break;
278         }
279
280         #if LOG_LEVEL >= LOG_LVL_INFO
281         /* Bound check */
282         if (seek_pos + offset > fd->fd.size)
283                 LOG_INFO("seek outside EOF\n");
284         #endif
285
286         fd->fd.seek_pos = seek_pos + offset;
287
288         return fd->fd.seek_pos - (FLASH_BASE + FLASH_BOOT_SIZE);
289 }
290
291 /**
292  * Reopen file \a fd
293  */
294 static struct KFile *flash_at91_reopen(struct KFile *_fd)
295 {
296         FlashAt91 *fd = FLASHAT91_CAST(_fd);
297         flash_at91_close(_fd);
298         flash_at91_open(fd);
299
300         return _fd;
301 }
302
303 /**
304  * Read from file \a fd \a size bytes and put it in buffer \a buf
305  * \return the number of bytes read.
306  */
307 static size_t flash_at91_read(struct KFile *_fd, void *_buf, size_t size)
308 {
309         FlashAt91 *fd = FLASHAT91_CAST(_fd);
310         uint8_t *buf =(uint8_t *)_buf;
311
312         size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
313
314         LOG_INFO("Reading at pos[%lu]\n", fd->fd.seek_pos);
315
316         // Flush current buffered page (if modified).
317         flash_at91_flush(fd);
318
319         uint32_t *addr = (uint32_t *)fd->fd.seek_pos;
320         memcpy(buf, (uint8_t *)addr, size);
321
322         fd->fd.seek_pos += size;
323
324         LOG_INFO("Read %u bytes\n", size);
325         return size;
326 }
327
328
329 /**
330  * Init module to perform write and read operation on internal
331  * flash memory.
332  */
333 void flash_at91_init(FlashAt91 *fd)
334 {
335         memset(fd, 0, sizeof(*fd));
336         DB(fd->fd._type = KFT_FLASHAT91);
337
338         // Set up flash programming functions.
339         fd->fd.reopen = flash_at91_reopen;
340         fd->fd.close = flash_at91_close;
341         fd->fd.write = flash_at91_write;
342         fd->fd.read = flash_at91_read;
343         fd->fd.seek = flash_at91_seek;
344         fd->fd.error = flash_at91_getStatus;
345         fd->fd.flush = flash_at91_kfileFlush;
346
347         flash_at91_open(fd);
348
349         uint32_t fmcn;
350         uint32_t fws = 0;
351
352
353         /*
354          * Compute values to insert into mode register.
355          */
356
357         /* main clocks in 1.5uS */
358         fmcn = (CPU_FREQ/1000000ul) + (CPU_FREQ/2000000ul) + 1;
359
360         /* hard overclocking */
361         if (fmcn > 0xFF)
362                 fmcn = 0xFF;
363
364         /* Only allow fmcn=0 if clock period is > 30 us = 33kHz. */
365         if (CPU_FREQ <= 33333ul)
366                 fmcn = 0;
367
368         /* Only allow fws=0 if clock frequency is < 30 MHz. */
369         if (CPU_FREQ > 30000000ul)
370         {
371                 fws = 1;
372         }
373
374         // Set wait states and number of MCK cycles in 1.5 usecs
375         MC_FMR = fmcn << 16 | fws << 8;
376
377 }