Silent nightlytest warning.
[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  * notest:arm
39  */
40
41 #include "flash_lpc2.h"
42 #include "cfg/cfg_emb_flash.h"
43
44 // Define log settings for cfg/log.h
45 #define LOG_LEVEL    CONFIG_FLASH_EMB_LOG_LEVEL
46 #define LOG_FORMAT   CONFIG_FLASH_EMB_LOG_FORMAT
47 #include <cfg/log.h>
48 #include <cfg/macros.h>
49
50 #include <cpu/irq.h>
51 #include <cpu/attr.h>
52 #include <cpu/power.h>
53 #include <cpu/types.h>
54
55 #include <io/kblock.h>
56 #include <io/arm.h>
57
58 #include <drv/timer.h>
59 #include <drv/flash.h>
60
61 #include <string.h>
62
63 /* Embedded flash programming defines. */
64 #define IAP_ADDRESS 0x7ffffff1
65
66 typedef enum IapCommands
67 {
68         PREPARE_SECTOR_FOR_WRITE = 50,
69         COPY_RAM_TO_FLASH = 51,
70         ERASE_SECTOR = 52,
71         BLANK_CHECK_SECTOR = 53,
72         READ_PART_ID = 54,
73         READ_BOOT_VER = 55,
74         COMPARE = 56,
75         REINVOKE_ISP = 57,
76 } IapCommands;
77
78 #if CPU_ARM_LPC2378
79         #define FLASH_MEM_SIZE         (504 * 1024L)
80         #define FLASH_PAGE_SIZE_BYTES   4096
81         #define FLASH_PAGE_4K_CNT         14
82 #else
83         #error Unknown CPU
84 #endif
85
86 #define CMD_SUCCESS 0
87
88 struct FlashHardware
89 {
90         uint8_t status;
91 };
92
93 typedef struct IapCmd
94 {
95         uint32_t cmd;
96         uint32_t param[4];
97 } IapCmd;
98
99 typedef struct IapRes
100 {
101         uint32_t status;
102         uint32_t res[2];
103 } IapRes;
104
105 typedef void (*iap_callback_t)(IapCmd *, IapRes *);
106
107 iap_callback_t iap = (iap_callback_t)IAP_ADDRESS;
108
109 static size_t sector_size(uint32_t page)
110 {
111         if (page < 8)
112                 return 4096;
113         else if (page < 22)
114                 return 32768;
115         else if (page < 28)
116                 return 4096;
117
118         ASSERT(0);
119         return 0;
120 }
121
122 static size_t sector_addr(uint32_t page)
123 {
124         if (page < 8)
125                 return page * 4096;
126         else if (page < 22)
127                 return (page - 8) * 32768 + 4096 * 8;
128         else if (page < 28)
129                 return (page - 22) * 4096 + 32768 * 14 + 4096 * 8;
130
131         ASSERT(0);
132         return 0;
133 }
134
135
136 static uint32_t addr_to_sector(size_t addr)
137 {
138         if (addr < 4096 * 8)
139                 return addr / 4096;
140         else if (addr < 4096 * 8 + 32768L * 14)
141                 return ((addr - 4096 * 8) / 32768) + 8;
142         else if (addr < 4096 * 8 + 32768L * 14 + 4096 * 6)
143                 return ((addr - 4096 * 8 - 32768L * 14) / 4096) + 22;
144
145         ASSERT(0);
146         return 0;
147 }
148
149 static uint32_t addr_to_pageaddr(size_t addr)
150 {
151         if (addr < 4096 * 8)
152                 return addr % 4096;
153         else if (addr < 4096 * 8 + 32768L * 14)
154                 return (addr - 4096 * 8) % 32768;
155         else if (addr < 4096 * 8 + 32768L * 14 + 4096 * 6)
156                 return (addr - 4096 * 8 - 32768L * 14) % 4096;
157
158         ASSERT(0);
159         return 0;
160 }
161
162 static size_t lpc2_flash_readDirect(struct KBlock *blk, block_idx_t idx, void *buf, size_t offset, size_t size)
163 {
164         ASSERT(offset == 0);
165         ASSERT(size == blk->blk_size);
166
167         ASSERT(sector_size(idx) <= FLASH_PAGE_SIZE_BYTES);
168
169         memcpy(buf, (void *)(idx * blk->blk_size), size);
170         return size;
171 }
172
173 static size_t lpc2_flash_writeDirect(struct KBlock *blk, block_idx_t idx, const void *_buf, size_t offset, size_t size)
174 {
175         ASSERT(offset == 0);
176         ASSERT(size == blk->blk_size);
177         ASSERT(sector_size(idx) <= FLASH_PAGE_SIZE_BYTES);
178
179         Flash *fls = FLASH_CAST(blk);
180         const uint8_t *buf = (const uint8_t *)_buf;
181         cpu_flags_t flags;
182
183         //Compute page address of current page.
184         uint32_t addr = sector_addr(idx);
185
186         LOG_INFO("Writing page %ld...\n", idx);
187
188         IRQ_SAVE_DISABLE(flags);
189
190         IapCmd cmd;
191         IapRes res;
192         cmd.cmd = PREPARE_SECTOR_FOR_WRITE;
193         cmd.param[0] = cmd.param[1] = idx;
194         iap(&cmd, &res);
195         if (res.status != CMD_SUCCESS)
196         {
197                 LOG_ERR("%ld\n", res.status);
198                 fls->hw->status |= FLASH_WR_ERR;
199                 return 0;
200         }
201
202         cmd.cmd = ERASE_SECTOR;
203         cmd.param[0] = cmd.param[1] = idx;
204         cmd.param[2] = CPU_FREQ / 1000;
205         iap(&cmd, &res);
206         if (res.status != CMD_SUCCESS)
207         {
208                 LOG_ERR("%ld\n", res.status);
209                 fls->hw->status |= FLASH_WR_ERR;
210                 return 0;
211         }
212
213         while (size)
214         {
215                 LOG_INFO("Writing page %ld, addr %ld, size %d\n", idx, addr, size);
216                 cmd.cmd = PREPARE_SECTOR_FOR_WRITE;
217                 cmd.param[0] = cmd.param[1] = idx;
218                 iap(&cmd, &res);
219                 if (res.status != CMD_SUCCESS)
220                 {
221                         LOG_ERR("%ld\n", res.status);
222                         fls->hw->status |= FLASH_WR_ERR;
223                         return 0;
224                 }
225
226                 cmd.cmd = COPY_RAM_TO_FLASH;
227                 cmd.param[0] = addr;
228                 cmd.param[1] = (uint32_t)buf;
229                 cmd.param[2] = 4096;
230                 cmd.param[3] = CPU_FREQ / 1000;
231                 iap(&cmd, &res);
232                 if (res.status != CMD_SUCCESS)
233                 {
234                         LOG_ERR("%ld\n", res.status);
235                         fls->hw->status |= FLASH_WR_ERR;
236                         return 0;
237                 }
238
239                 size -= 4096;
240                 addr += 4096;
241                 buf += 4096 / sizeof(uint32_t);
242         }
243
244         IRQ_RESTORE(flags);
245         LOG_INFO("Done\n");
246
247         return blk->blk_size;
248 }
249
250 static int lpc2_flash_error(struct KBlock *blk)
251 {
252         Flash *fls = FLASH_CAST(blk);
253         return fls->hw->status;
254 }
255
256 static void lpc2_flash_clearerror(struct KBlock *blk)
257 {
258         Flash *fls = FLASH_CAST(blk);
259         fls->hw->status = 0;
260 }
261
262 static const KBlockVTable flash_lpc2_buffered_vt =
263 {
264         .readDirect = lpc2_flash_readDirect,
265         .writeDirect = lpc2_flash_writeDirect,
266
267         .readBuf = kblock_swReadBuf,
268         .writeBuf = kblock_swWriteBuf,
269         .load = kblock_swLoad,
270         .store = kblock_swStore,
271
272         .close = kblock_swClose,
273
274         .error = lpc2_flash_error,
275         .clearerr = lpc2_flash_clearerror,
276 };
277
278 static const KBlockVTable flash_lpc2_unbuffered_vt =
279 {
280         .readDirect = lpc2_flash_readDirect,
281         .writeDirect = lpc2_flash_writeDirect,
282
283         .close = kblock_swClose,
284
285         .error = lpc2_flash_error,
286         .clearerr = lpc2_flash_clearerror,
287 };
288
289 static struct FlashHardware flash_lpc2_hw;
290 static uint8_t flash_buf[FLASH_PAGE_SIZE_BYTES];
291
292 static void common_init(Flash *fls)
293 {
294         memset(fls, 0, sizeof(*fls));
295         DB(fls->blk.priv.type = KBT_FLASH);
296
297         fls->hw = &flash_lpc2_hw;
298
299         fls->blk.blk_size = FLASH_PAGE_SIZE_BYTES;
300         fls->blk.blk_cnt = 28;
301 }
302
303 void flash_hw_init(Flash *fls)
304 {
305         common_init(fls);
306         fls->blk.priv.vt = &flash_lpc2_buffered_vt;
307         fls->blk.priv.flags |= KB_BUFFERED | KB_PARTIAL_WRITE;
308         fls->blk.priv.buf = flash_buf;
309
310         /* Load the first block in the cache */
311         void *flash_start = 0x0;
312         memcpy(fls->blk.priv.buf, flash_start, fls->blk.blk_size);
313
314         kprintf("page[%d]\n", sector_addr(22));
315 }
316
317 void flash_hw_initUnbuffered(Flash *fls)
318 {
319         common_init(fls);
320         fls->blk.priv.vt = &flash_lpc2_unbuffered_vt;
321 }