Move kfile interface to the io/ directory.
[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 <io/kfile.h>
58
59 #include <io/arm.h>
60
61 #include <drv/timer.h>
62 #include <drv/flash.h>
63
64 #include <string.h>
65
66 #define FLASH_START_PAGE DIV_ROUNDUP(FLASH_BOOT_SIZE, FLASH_PAGE_SIZE_BYTES)
67 #define FLASH_START_ADDR (FLASH_START_PAGE * FLASH_PAGE_SIZE_BYTES)
68
69 /**
70  * Really send the flash write command.
71  * 
72  * \note This function has to be placed in RAM because 
73  *       executing code from flash while a writing process
74  *       is in progress is forbidden.
75  */ 
76 RAM_FUNC NOINLINE static void write_page(uint32_t page)
77 {
78         // Send the 'write page' command
79         MC_FCR = MC_KEY | MC_FCMD_WP | (MC_PAGEN_MASK & (page << 8));
80
81         // Wait for the end of command
82         while(!(MC_FSR & BV(MC_FRDY)))
83         {
84                 //NOP;
85         }
86 }
87
88
89 /**
90  * Send write command.
91  *
92  * After WR command cpu write bufferd page into flash memory.
93  * 
94  */
95 INLINE void flash_at91_sendWRcmd(uint32_t page)
96 {
97         cpu_flags_t flags;
98
99         LOG_INFO("Writing page %ld...\n", page);
100
101         IRQ_SAVE_DISABLE(flags);
102         write_page(page);
103
104         IRQ_RESTORE(flags);
105         LOG_INFO("Done\n");
106 }
107
108 /**
109  * Return 0 if no error are occurred after flash memory
110  * read or write operation, otherwise return error code.
111  */
112 static int flash_at91_getStatus(UNUSED_ARG(struct KFile *, _fd))
113 {
114         /*
115          * This bit is set to one if we programming of at least one locked lock
116          * region.
117          */
118         if(MC_FSR & BV(MC_LOCKE))
119                 return -1;
120
121         /*
122          * This bit is set to one if an invalid command and/or a bad keywords was/were
123          * written in the Flash Command Register.
124          */
125         if(MC_FSR & BV(MC_PROGE))
126                 return -2;
127
128         return 0;
129 }
130
131
132 /**
133  * Write modified page on internal latch, and then send write command to
134  * flush page to internal flash.
135  */
136 static int flash_at91_flush(KFile *_fd)
137 {
138         Flash *fd = FLASH_CAST(_fd);
139         if (fd->page_dirty)
140         {
141                 //Compute page address of current page.
142                 page_addr_t *addr = (page_addr_t *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE);
143
144                 //Copy modified page into internal latch.
145                 for (page_addr_t page_addr = 0; page_addr < FLASH_PAGE_SIZE_BYTES; page_addr += 4)
146                 {
147                         // This is needed in order to have a single 32bit write instruction in addr.
148                         // (8 and 16 writes cause unpredictable results).
149                         uint32_t data;
150                         memcpy(&data, &fd->page_buf[page_addr], sizeof(data));
151                         *addr++ = data;
152                 }
153
154                 // Send write command to transfer page from latch to internal flash memory.
155                 flash_at91_sendWRcmd(fd->curr_page);
156                 fd->page_dirty = false;
157         }
158         return 0;
159 }
160
161
162 /**
163  * Check current page and if \a page is different, load it in
164  * temporary buffer.
165  */
166 static void flash_at91_loadPage(Flash *fd, page_t page)
167 {
168         if (page != fd->curr_page)
169         {
170                 flash_at91_flush(&fd->fd);
171                 // Load page
172                 memcpy(fd->page_buf, (char *)((page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES);
173                 fd->curr_page = page;
174                 LOG_INFO("Loaded page %lu\n", fd->curr_page);
175         }
176 }
177
178
179 /**
180  * Write program memory.
181  * Write \a size bytes from buffer \a _buf to file \a fd
182  * \note Write operations are buffered.
183  */
184 static size_t flash_at91_write(struct KFile *_fd, const void *_buf, size_t size)
185 {
186         Flash *fd = FLASH_CAST(_fd);
187         const uint8_t *buf =(const uint8_t *)_buf;
188
189         page_t page;
190         page_addr_t page_addr;
191         size_t total_write = 0;
192
193         size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
194
195         LOG_INFO("Writing at pos[%lu]\n", fd->fd.seek_pos);
196         while (size)
197         {
198                 page = (fd->fd.seek_pos + FLASH_START_ADDR) / FLASH_PAGE_SIZE_BYTES;
199                 page_addr = (fd->fd.seek_pos + FLASH_START_ADDR) % FLASH_PAGE_SIZE_BYTES;
200
201                 flash_at91_loadPage(fd, page);
202
203                 size_t wr_len = MIN(size, (size_t)(FLASH_PAGE_SIZE_BYTES - page_addr));
204
205                 memcpy(fd->page_buf + page_addr, buf, wr_len);
206                 fd->page_dirty = true;
207
208                 buf += wr_len;
209                 fd->fd.seek_pos += wr_len;
210                 size -= wr_len;
211                 total_write += wr_len;
212         }
213         LOG_INFO("written %u bytes\n", total_write);
214         return total_write;
215 }
216
217 /**
218  * Open flash file \a fd
219  * \a name and \a mode are unused, cause flash memory is
220  * threated like one file.
221  */
222 static void flash_at91_open(struct Flash *fd)
223 {
224         fd->curr_page = FLASH_START_PAGE;
225         fd->fd.size = FLASH_MEM_SIZE - fd->curr_page * FLASH_PAGE_SIZE_BYTES;
226         fd->fd.seek_pos = 0;
227         
228         memcpy(fd->page_buf, (char *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES);
229
230         fd->page_dirty = false;
231         LOG_INFO("Flash file opened, pos %ld, size %ld\n", fd->fd.seek_pos, fd->fd.size);
232 }
233
234 /**
235  * Read from file \a fd \a size bytes and put it in buffer \a buf
236  * \return the number of bytes read.
237  */
238 static size_t flash_at91_read(struct KFile *_fd, void *_buf, size_t size)
239 {
240         Flash *fd = FLASH_CAST(_fd);
241         uint8_t *buf =(uint8_t *)_buf;
242
243         size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
244
245         LOG_INFO("Reading at pos[%lu]\n", fd->fd.seek_pos);
246
247         // Flush current buffered page (if modified).
248         flash_at91_flush(&fd->fd);
249
250         kfile_off_t *addr = (kfile_off_t *)(fd->fd.seek_pos + FLASH_START_ADDR);
251         memcpy(buf, addr, size);
252
253         fd->fd.seek_pos += size;
254
255         LOG_INFO("Read %u bytes\n", size);
256         return size;
257 }
258
259
260 /**
261  * Init module to perform write and read operation on internal
262  * flash memory.
263  */
264 void flash_hw_init(struct Flash *fd)
265 {
266         memset(fd, 0, sizeof(*fd));
267         // Init base class.
268         kfile_init(&fd->fd);
269         DB(fd->fd._type = KFT_FLASH);
270
271         // Set up flash programming functions.
272         fd->fd.write = flash_at91_write;
273         fd->fd.read = flash_at91_read;
274         fd->fd.error = flash_at91_getStatus;
275         fd->fd.flush = flash_at91_flush;
276
277         flash_at91_open(fd);
278 }