Remove redundant code.
[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 #include <drv/flash.h>
63
64 #include <string.h>
65
66 /*
67  * Check if flash memory is ready to accept other commands.
68  */
69 RAM_FUNC static bool flash_at91_isReady(void)
70 {
71         return (MC_FSR & BV(MC_FRDY));
72 }
73
74 /**
75  * Send write command.
76  *
77  * After WR command cpu write bufferd page into flash memory.
78  */
79 RAM_FUNC static void flash_at91_sendWRcmd(uint32_t page)
80 {
81         cpu_flags_t flags;
82
83         // Wait for end of command
84         while(!flash_at91_isReady())
85         {
86                 cpu_relax();
87         }
88
89         IRQ_SAVE_DISABLE(flags);
90
91         // Send the 'write page' command
92         MC_FCR = MC_KEY | MC_FCMD_WP | (MC_PAGEN_MASK & (page << 8));
93
94         // Wait for end of command
95         while(!flash_at91_isReady())
96         {
97                 cpu_relax();
98         }
99
100         IRQ_RESTORE(flags);
101 }
102
103 /**
104  * Return 0 if no error are occurred after flash memory
105  * read or write operation, otherwise return error code.
106  */
107 RAM_FUNC static int flash_at91_getStatus(struct KFile *_fd)
108 {
109         (void)_fd;
110
111
112         /*
113          * This bit is set to one if we programming of at least one locked lock
114          * region.
115          */
116         if(MC_FSR & BV(MC_LOCKE))
117                 return -1;
118
119         /*
120          * This bit is set to one if an invalid command and/or a bad keywords was/were
121          * written in the Flash Command Register.
122          */
123         if(MC_FSR & BV(MC_PROGE))
124                 return -2;
125
126         return 0;
127 }
128
129
130 /**
131  * Write modified page on internal latch, and then send write command to
132  * flush page to internal flash.
133  */
134 RAM_FUNC static void flash_at91_flush(Flash *fd)
135 {
136         if (fd->page_dirty)
137         {
138                 //Compute page address of current page.
139                 page_addr_t *addr = (page_addr_t *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE);
140
141                 //Copy modified page into internal latch.
142                 for (page_addr_t page_addr = 0; page_addr < FLASH_PAGE_SIZE_BYTES; page_addr += 4)
143                 {
144                         uint32_t data;
145                         memcpy(&data, &fd->page_buf[page_addr], sizeof(data));
146                         *addr = data;
147                         addr++;
148                 }
149
150                 // Send write command to transfer page from latch to internal flash memory.
151                 flash_at91_sendWRcmd(fd->curr_page);
152         }
153 }
154
155 /**
156  * Flush At91 flash function.
157  *
158  * Write current buffered page in flash memory (if modified).
159  * This function erase flash memory page before writing.
160  */
161 static int flash_at91_kfileFlush(struct KFile *_fd)
162 {
163         Flash *fd = FLASH_CAST(_fd);
164         flash_at91_flush(fd);
165         return 0;
166 }
167
168
169 /**
170  * Check current page and if \a page is different, load it in
171  * temporary buffer.
172  */
173 static void flash_at91_loadPage(Flash *fd, page_t page)
174 {
175         if (page != fd->curr_page)
176         {
177                 flash_at91_flush(fd);
178                 // Load page
179                 memcpy(fd->page_buf, (const char *)((page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES);
180                 fd->curr_page = page;
181                 LOG_INFO("Loaded page %lu\n", fd->curr_page);
182         }
183 }
184
185
186 /**
187  * Write program memory.
188  * Write \a size bytes from buffer \a _buf to file \a fd
189  * \note Write operations are buffered.
190  */
191 static size_t flash_at91_write(struct KFile *_fd, const void *_buf, size_t size)
192 {
193         Flash *fd = FLASH_CAST(_fd);
194         const uint8_t *buf =(const uint8_t *)_buf;
195
196         page_t page;
197         page_addr_t page_addr;
198         size_t total_write = 0;
199
200         size = MIN((kfile_off_t)size, (kfile_off_t)(fd->fd.size - (fd->fd.seek_pos - FLASH_BASE)));
201
202         LOG_INFO("Writing at pos[%lu]\n", fd->fd.seek_pos);
203         while (size)
204         {
205                 page = (fd->fd.seek_pos - FLASH_BASE) / FLASH_PAGE_SIZE_BYTES;
206                 page_addr = (fd->fd.seek_pos - FLASH_BASE) % FLASH_PAGE_SIZE_BYTES;
207
208                 flash_at91_loadPage(fd, page);
209
210                 size_t wr_len = MIN(size, (size_t)(FLASH_PAGE_SIZE_BYTES - page_addr));
211
212                 memcpy(fd->page_buf + page_addr, buf, wr_len);
213                 fd->page_dirty = true;
214
215                 buf += wr_len;
216                 fd->fd.seek_pos += wr_len;
217                 size -= wr_len;
218                 total_write += wr_len;
219         }
220         LOG_INFO("written %u bytes\n", total_write);
221         return total_write;
222 }
223
224 /**
225  * Close file \a fd
226  */
227 static int flash_at91_close(struct KFile *_fd)
228 {
229         Flash *fd = FLASH_CAST(_fd);
230         flash_at91_flush(fd);
231         LOG_INFO("Flash file closed\n");
232
233         return 0;
234 }
235
236 /**
237  * Open flash file \a fd
238  * \a name and \a mode are unused, cause flash memory is
239  * threated like one file.
240  */
241 static void flash_at91_open(struct Flash *fd)
242 {
243         fd->fd.size = FLASH_BASE + FLASH_MEM_SIZE;
244         fd->fd.seek_pos = FLASH_BASE + FLASH_BOOT_SIZE;
245         fd->curr_page = (fd->fd.seek_pos - FLASH_BASE) / FLASH_PAGE_SIZE_BYTES;
246
247         memcpy(fd->page_buf, (const char *)((fd->curr_page * FLASH_PAGE_SIZE_BYTES) + FLASH_BASE), FLASH_PAGE_SIZE_BYTES);
248
249         fd->page_dirty = false;
250         LOG_INFO("Flash file opened\n");
251 }
252
253
254 /**
255  * Move \a fd file seek position of \a offset bytes from \a whence.
256  *
257  */
258 static kfile_off_t flash_at91_seek(struct KFile *_fd, kfile_off_t offset, KSeekMode whence)
259 {
260         Flash *fd = FLASH_CAST(_fd);
261         kfile_off_t seek_pos;
262
263         switch (whence)
264         {
265
266         case KSM_SEEK_SET:
267                 seek_pos = FLASH_BASE + FLASH_BOOT_SIZE;
268                 break;
269         case KSM_SEEK_END:
270                 seek_pos = fd->fd.size;
271                 break;
272         case KSM_SEEK_CUR:
273                 seek_pos = fd->fd.seek_pos;
274                 break;
275         default:
276                 ASSERT(0);
277                 return EOF;
278                 break;
279         }
280
281         #if LOG_LEVEL >= LOG_LVL_INFO
282         /* Bound check */
283         if (seek_pos + offset > fd->fd.size)
284                 LOG_INFO("seek outside EOF\n");
285         #endif
286
287         fd->fd.seek_pos = seek_pos + offset;
288
289         return fd->fd.seek_pos - (FLASH_BASE + FLASH_BOOT_SIZE);
290 }
291
292 /**
293  * Reopen file \a fd
294  */
295 static struct KFile *flash_at91_reopen(struct KFile *_fd)
296 {
297         Flash *fd = FLASH_CAST(_fd);
298         flash_at91_close(_fd);
299         flash_at91_open(fd);
300
301         return _fd;
302 }
303
304 /**
305  * Read from file \a fd \a size bytes and put it in buffer \a buf
306  * \return the number of bytes read.
307  */
308 static size_t flash_at91_read(struct KFile *_fd, void *_buf, size_t size)
309 {
310         Flash *fd = FLASH_CAST(_fd);
311         uint8_t *buf =(uint8_t *)_buf;
312
313         size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
314
315         LOG_INFO("Reading at pos[%lu]\n", fd->fd.seek_pos);
316
317         // Flush current buffered page (if modified).
318         flash_at91_flush(fd);
319
320         uint32_t *addr = (uint32_t *)fd->fd.seek_pos;
321         memcpy(buf, (uint8_t *)addr, size);
322
323         fd->fd.seek_pos += size;
324
325         LOG_INFO("Read %u bytes\n", size);
326         return size;
327 }
328
329
330 /**
331  * Init module to perform write and read operation on internal
332  * flash memory.
333  */
334 void flash_hw_init(struct Flash *fd)
335 {
336         memset(fd, 0, sizeof(*fd));
337         DB(fd->fd._type = KFT_FLASH);
338
339         // Set up flash programming functions.
340         fd->fd.reopen = flash_at91_reopen;
341         fd->fd.close = flash_at91_close;
342         fd->fd.write = flash_at91_write;
343         fd->fd.read = flash_at91_read;
344         fd->fd.seek = flash_at91_seek;
345         fd->fd.error = flash_at91_getStatus;
346         fd->fd.flush = flash_at91_kfileFlush;
347
348         flash_at91_open(fd);
349 }