4 * This file is part of BeRTOS.
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.
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.
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
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.
29 * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
33 * \author Francesco Sacchi <batt@develer.com>
34 * \author Daniele Basile <asterix@develer.com>
36 * \brief NPX lpc23xx embedded flash read/write driver.
39 #include "flash_lpc2.h"
40 #include "cfg/cfg_emb_flash.h"
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
46 #include <cfg/macros.h>
50 #include <cpu/power.h>
52 #include <kern/kfile.h>
56 #include <drv/timer.h>
57 #include <drv/flash.h>
62 #define IAP_ADDRESS 0x7ffffff1
64 typedef enum IapCommands
66 PREPARE_SECTOR_FOR_WRITE = 50,
67 COPY_RAM_TO_FLASH = 51,
69 BLANK_CHECK_SECTOR = 53,
91 typedef void (*iap_callback_t)(IapCmd *, IapRes *);
93 iap_callback_t iap=(iap_callback_t)IAP_ADDRESS;
95 #define FLASH_MEM_SIZE (504 * 1024L)
97 static size_t flash_start_addr;
99 static size_t sector_size(page_t page)
112 static size_t sector_addr(page_t page)
117 return (page - 8) * 32768 + 4096 * 8;
119 return (page - 22) * 4096 + 32768 * 14 + 4096 * 8;
126 static page_t addr_to_sector(size_t addr)
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;
139 static page_addr_t addr_to_pageaddr(size_t addr)
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;
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;
158 * Send write command.
160 * After WR command cpu write bufferd page into flash memory.
163 static void flash_lpc2_writePage(page_t page, uint32_t *buf)
167 //Compute page address of current page.
168 page_addr_t addr = sector_addr(page);
170 LOG_INFO("Writing page %ld...\n", page);
172 IRQ_SAVE_DISABLE(flags);
176 cmd.cmd = PREPARE_SECTOR_FOR_WRITE;
177 cmd.param[0] = cmd.param[1] = page;
179 if (res.status != CMD_SUCCESS)
180 LOG_ERR("%ld\n", res.status);
182 cmd.cmd = ERASE_SECTOR;
183 cmd.param[0] = cmd.param[1] = page;
184 cmd.param[2] = CPU_FREQ / 1000;
186 if (res.status != CMD_SUCCESS)
187 LOG_ERR("%ld\n", res.status);
189 size_t size = sector_size(page);
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;
197 if (res.status != CMD_SUCCESS)
198 LOG_ERR("%ld\n", res.status);
200 cmd.cmd = COPY_RAM_TO_FLASH;
202 cmd.param[1] = (uint32_t)buf;
204 cmd.param[3] = CPU_FREQ / 1000;
206 if (res.status != CMD_SUCCESS)
207 LOG_ERR("%ld\n", res.status);
211 buf += 4096 / sizeof(uint32_t);
219 * Write modified page on internal latch, and then send write command to
220 * flush page to internal flash.
222 static int flash_lpc2_flush(UNUSED_ARG(KFile *, _fd))
226 flash_lpc2_writePage(curr_page, page_buf);
234 * Check current page and if \a page is different, load it in
237 static void flash_lpc2_loadPage(KFile *fd, page_t page)
239 if (page != curr_page)
241 flash_lpc2_flush(fd);
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);
247 memcpy(page_buf, (char *)addr, size);
249 LOG_INFO("Loaded page %lu\n", curr_page);
254 * Write program memory.
255 * Write \a size bytes from buffer \a _buf to file \a fd
256 * \note Write operations are buffered.
258 static size_t flash_lpc2_write(struct KFile *fd, const void *_buf, size_t size)
260 const uint8_t *buf =(const uint8_t *)_buf;
263 page_addr_t page_addr;
264 size_t total_write = 0;
266 size = MIN((kfile_off_t)size, fd->size - fd->seek_pos);
268 LOG_INFO("Writing at pos[%lu]\n", fd->seek_pos);
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);
275 flash_lpc2_loadPage(fd, page);
277 size_t wr_len = MIN(size, (size_t)(sector_size(page) - page_addr));
279 memcpy(((uint8_t *)page_buf) + page_addr, buf, wr_len);
283 fd->seek_pos += wr_len;
285 total_write += wr_len;
287 LOG_INFO("written %u bytes\n", total_write);
292 * Open flash file \a fd
293 * \a name and \a mode are unused, cause flash memory is
294 * threated like one file.
296 static void flash_lpc2_open(struct FlashLpc2 *fd)
298 curr_page = addr_to_sector(FLASH_BOOT_SIZE);
299 if (addr_to_pageaddr(FLASH_BOOT_SIZE))
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);
305 fd->fd.size = FLASH_MEM_SIZE - sector_addr(curr_page);
308 memcpy(page_buf, (char *)sector_addr(curr_page), sector_size(curr_page));
311 LOG_INFO("Flash file opened, pos %ld, size %ld\n", fd->fd.seek_pos, fd->fd.size);
315 * Read from file \a fd \a size bytes and put it in buffer \a buf
316 * \return the number of bytes read.
318 static size_t flash_lpc2_read(struct KFile *fd, void *_buf, size_t size)
320 uint8_t *buf =(uint8_t *)_buf;
322 size = MIN((kfile_off_t)size, fd->size - fd->seek_pos);
324 LOG_INFO("Reading at pos[%lu]\n", fd->seek_pos);
326 // Flush current buffered page (if modified).
327 flash_lpc2_flush(fd);
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);
333 for (unsigned i = 0; i< size; i++)
338 kprintf("%02x ", buf[i]);
342 fd->seek_pos += size;
344 LOG_INFO("Read %u bytes\n", size);
350 * Init module to perform write and read operation on internal
353 void flash_hw_init(struct FlashLpc2 *fd)
355 memset(fd, 0, sizeof(*fd));
358 DB(fd->fd._type = KFT_FLASH);
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;