f3c60f1fd28a763f59e1620615e6cd00176786a4
[bertos.git] / bertos / cpu / arm / drv / flash_lpc2.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  * \author Francesco Sacchi <batt@develer.com>
34  * \author Daniele Basile <asterix@develer.com>
35  *
36  * \brief NPX lpc23xx embedded flash read/write driver.
37  */
38
39 #include "flash_lpc2.h"
40 #include "cfg/cfg_emb_flash.h"
41
42 // Define log settings for cfg/log.h
43 #define LOG_LEVEL    CONFIG_FLASH_EMB_LOG_LEVEL
44 #define LOG_FORMAT   CONFIG_FLASH_EMB_LOG_FORMAT
45 #include <cfg/log.h>
46 #include <cfg/macros.h>
47
48 #include <cpu/irq.h>
49 #include <cpu/attr.h>
50 #include <cpu/power.h>
51
52 #include <kern/kfile.h>
53
54 #include <io/arm.h>
55
56 #include <drv/timer.h>
57 #include <drv/flash.h>
58
59 #include <string.h>
60
61
62 #define IAP_ADDRESS 0x7ffffff1
63
64 typedef enum IapCommands
65 {
66         PREPARE_SECTOR_FOR_WRITE = 50,
67         COPY_RAM_TO_FLASH = 51,
68         ERASE_SECTOR = 52,
69         BLANK_CHECK_SECTOR = 53,
70         READ_PART_ID = 54,
71         READ_BOOT_VER = 55,
72         COMPARE = 56,
73         REINVOKE_ISP = 57,
74 } IapCommands;
75
76
77 #define CMD_SUCCESS 0
78
79 typedef struct IapCmd
80 {
81         uint32_t cmd;
82         uint32_t param[4];
83 } IapCmd;
84
85 typedef struct IapRes
86 {
87         uint32_t status;
88         uint32_t res[2];
89 } IapRes;
90
91 typedef void (*iap_callback_t)(IapCmd *, IapRes *);
92
93 iap_callback_t iap=(iap_callback_t)IAP_ADDRESS;
94
95 #define FLASH_MEM_SIZE (504 * 1024L)
96
97 static size_t flash_start_addr;
98
99 static size_t sector_size(page_t page)
100 {
101         if (page < 8)
102                 return 4096;
103         else if (page < 22)
104                 return 32768;
105         else if (page < 28)
106                 return 4096;
107
108         ASSERT(0);
109         return 0;
110 }
111
112 static size_t sector_addr(page_t page)
113 {
114         if (page < 8)
115                 return page * 4096;
116         else if (page < 22)
117                 return (page - 8) * 32768 + 4096 * 8;
118         else if (page < 28)
119                 return (page - 22) * 4096 + 32768 * 14 + 4096 * 8;
120
121         ASSERT(0);
122         return 0;
123 }
124
125
126 static page_t addr_to_sector(size_t addr)
127 {
128         if (addr < 4096 * 8)
129                 return addr / 4096;
130         else if (addr < 4096 * 8 + 32768L * 14)
131                 return ((addr - 4096 * 8) / 32768) + 8;
132         else if (addr < 4096 * 8 + 32768L * 14 + 4096 * 6)
133                 return ((addr - 4096 * 8 - 32768L * 14) / 4096) + 22;
134
135         ASSERT(0);
136         return 0;
137 }
138
139 static page_addr_t addr_to_pageaddr(size_t addr)
140 {
141         if (addr < 4096 * 8)
142                 return addr % 4096;
143         else if (addr < 4096 * 8 + 32768L * 14)
144                 return (addr - 4096 * 8) % 32768;
145         else if (addr < 4096 * 8 + 32768L * 14 + 4096 * 6)
146                 return (addr - 4096 * 8 - 32768L * 14) % 4096;
147
148         ASSERT(0);
149         return 0;
150 }
151
152
153 static uint32_t page_buf[1024];//MAX_FLASH_PAGE_SIZE / sizeof(uint32_t)];
154 static bool page_dirty;
155 static page_t curr_page;
156
157 /**
158  * Send write command.
159  *
160  * After WR command cpu write bufferd page into flash memory.
161  *
162  */
163 static void flash_lpc2_writePage(page_t page, uint32_t *buf)
164 {
165         cpu_flags_t flags;
166
167         //Compute page address of current page.
168         page_addr_t addr = sector_addr(page);
169
170         LOG_INFO("Writing page %ld...\n", page);
171
172         IRQ_SAVE_DISABLE(flags);
173
174         IapCmd cmd;
175         IapRes res;
176         cmd.cmd = PREPARE_SECTOR_FOR_WRITE;
177         cmd.param[0] = cmd.param[1] = page;
178         iap(&cmd, &res);
179         if (res.status != CMD_SUCCESS)
180                 LOG_ERR("%ld\n", res.status);
181
182         cmd.cmd = ERASE_SECTOR;
183         cmd.param[0] = cmd.param[1] = page;
184         cmd.param[2] = CPU_FREQ / 1000;
185         iap(&cmd, &res);
186         if (res.status != CMD_SUCCESS)
187                 LOG_ERR("%ld\n", res.status);
188
189         size_t size = sector_size(page);
190
191         while (size)
192         {
193                 LOG_INFO("Writing page %ld, addr %ld, size %d\n", page, addr, size);
194                 cmd.cmd = PREPARE_SECTOR_FOR_WRITE;
195                 cmd.param[0] = cmd.param[1] = page;
196                 iap(&cmd, &res);
197                 if (res.status != CMD_SUCCESS)
198                         LOG_ERR("%ld\n", res.status);
199
200                 cmd.cmd = COPY_RAM_TO_FLASH;
201                 cmd.param[0] = addr;
202                 cmd.param[1] = (uint32_t)buf;
203                 cmd.param[2] = 4096;
204                 cmd.param[3] = CPU_FREQ / 1000;
205                 iap(&cmd, &res);
206                 if (res.status != CMD_SUCCESS)
207                         LOG_ERR("%ld\n", res.status);
208
209                 size -= 4096;
210                 addr += 4096;
211                 buf += 4096 / sizeof(uint32_t);
212         }
213
214         IRQ_RESTORE(flags);
215         LOG_INFO("Done\n");
216 }
217
218 /**
219  * Write modified page on internal latch, and then send write command to
220  * flush page to internal flash.
221  */
222 static int flash_lpc2_flush(UNUSED_ARG(KFile *, _fd))
223 {
224         if (page_dirty)
225         {
226                 flash_lpc2_writePage(curr_page, page_buf);
227                 page_dirty = false;
228         }
229         return 0;
230 }
231
232
233 /**
234  * Check current page and if \a page is different, load it in
235  * temporary buffer.
236  */
237 static void flash_lpc2_loadPage(KFile *fd, page_t page)
238 {
239         if (page != curr_page)
240         {
241                 flash_lpc2_flush(fd);
242
243                 size_t addr = sector_addr(page);
244                 size_t size = sector_size(page);
245                 LOG_INFO("page %ld, addr %d, size %d\n", page, addr, size);
246                 // Load page
247                 memcpy(page_buf, (char *)addr, size);
248                 curr_page = page;
249                 LOG_INFO("Loaded page %lu\n", curr_page);
250         }
251 }
252
253 /**
254  * Write program memory.
255  * Write \a size bytes from buffer \a _buf to file \a fd
256  * \note Write operations are buffered.
257  */
258 static size_t flash_lpc2_write(struct KFile *fd, const void *_buf, size_t size)
259 {
260         const uint8_t *buf =(const uint8_t *)_buf;
261
262         page_t page;
263         page_addr_t page_addr;
264         size_t total_write = 0;
265
266         size = MIN((kfile_off_t)size, fd->size - fd->seek_pos);
267
268         LOG_INFO("Writing at pos[%lu]\n", fd->seek_pos);
269         while (size)
270         {
271                 page = addr_to_sector(fd->seek_pos + flash_start_addr);
272                 page_addr = addr_to_pageaddr(fd->seek_pos + flash_start_addr);
273                 LOG_INFO("addr %ld, page %ld, page_addr %ld\n", fd->seek_pos + flash_start_addr, page, page_addr);
274
275                 flash_lpc2_loadPage(fd, page);
276
277                 size_t wr_len = MIN(size, (size_t)(sector_size(page) - page_addr));
278
279                 memcpy(((uint8_t *)page_buf) + page_addr, buf, wr_len);
280                 page_dirty = true;
281
282                 buf += wr_len;
283                 fd->seek_pos += wr_len;
284                 size -= wr_len;
285                 total_write += wr_len;
286         }
287         LOG_INFO("written %u bytes\n", total_write);
288         return total_write;
289 }
290
291 /**
292  * Open flash file \a fd
293  * \a name and \a mode are unused, cause flash memory is
294  * threated like one file.
295  */
296 static void flash_lpc2_open(struct FlashLpc2 *fd)
297 {
298         curr_page = addr_to_sector(FLASH_BOOT_SIZE);
299         if (addr_to_pageaddr(FLASH_BOOT_SIZE))
300                 curr_page++;
301
302         flash_start_addr = sector_addr(curr_page);
303         LOG_INFO("Boot size %d, curr_page %ld, flash_start_addr %d\n", FLASH_BOOT_SIZE, curr_page, flash_start_addr);
304
305         fd->fd.size = FLASH_MEM_SIZE - sector_addr(curr_page);
306         fd->fd.seek_pos = 0;
307
308         memcpy(page_buf, (char *)sector_addr(curr_page), sector_size(curr_page));
309
310         page_dirty = false;
311         LOG_INFO("Flash file opened, pos %ld, size %ld\n", fd->fd.seek_pos, fd->fd.size);
312 }
313
314 /**
315  * Read from file \a fd \a size bytes and put it in buffer \a buf
316  * \return the number of bytes read.
317  */
318 static size_t flash_lpc2_read(struct KFile *fd, void *_buf, size_t size)
319 {
320         uint8_t *buf =(uint8_t *)_buf;
321
322         size = MIN((kfile_off_t)size, fd->size - fd->seek_pos);
323
324         LOG_INFO("Reading at pos[%lu]\n", fd->seek_pos);
325
326         // Flush current buffered page (if modified).
327         flash_lpc2_flush(fd);
328
329         kfile_off_t *addr = (kfile_off_t *)(fd->seek_pos + flash_start_addr);
330         LOG_INFO("actual addr %ld\n", (uint32_t)addr);
331         memcpy(buf, addr, size);
332
333         for (unsigned i = 0; i< size; i++)
334         {
335                 if (i % 16 == 0)
336                         kputchar('\n');
337
338                 kprintf("%02x ", buf[i]);
339         }
340         kputchar('\n');
341
342         fd->seek_pos += size;
343
344         LOG_INFO("Read %u bytes\n", size);
345         return size;
346 }
347
348
349 /**
350  * Init module to perform write and read operation on internal
351  * flash memory.
352  */
353 void flash_hw_init(struct FlashLpc2 *fd)
354 {
355         memset(fd, 0, sizeof(*fd));
356         // Init base class.
357         kfile_init(&fd->fd);
358         DB(fd->fd._type = KFT_FLASH);
359
360         // Set up flash programming functions.
361         fd->fd.write = flash_lpc2_write;
362         fd->fd.read = flash_lpc2_read;
363         fd->fd.flush = flash_lpc2_flush;
364
365         flash_lpc2_open(fd);
366         TRACE;
367 }