Move kfile interface to the io/ directory.
[bertos.git] / bertos / cpu / avr / drv / flash_avr.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 2007 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief Self programming routines.
34  *
35  * \author Francesco Sacchi <batt@develer.com>
36  * \author Daniele Basile <asterix@develer.com>
37  *
38  * This module implements a kfile-like access for Atmel avr
39  * internal flash.
40  * Internal flash writing access is controlled by BOOTSZ fuses, check
41  * datasheet for details.
42  */
43
44 #include "flash_avr.h"
45
46 #include "cfg/cfg_flash_avr.h"
47 #include <cfg/macros.h> // MIN()
48 #include <cfg/compiler.h>
49 #include <cfg/debug.h>
50 #include <cpu/irq.h>
51
52 // Define log settings for cfg/log.h
53 #define LOG_LEVEL    CONFIG_FLASH_AVR_LOG_LEVEL
54 #define LOG_FORMAT   CONFIG_FLASH_AVR_LOG_FORMAT
55 #include <cfg/log.h>
56
57 #include <drv/wdt.h>
58 #include <drv/flash.h>
59
60 #include <io/kfile.h>
61
62 #include <avr/io.h>
63 #include <avr/boot.h>
64 #include <avr/pgmspace.h>
65
66 #include <string.h>
67
68
69 /**
70  * Definition of type for avr flash module.
71  */
72 typedef uint16_t page_addr_t;
73
74
75 /**
76  * Private avr flush funtion.
77  *
78  * Write current buffered page in flash memory (if modified).
79  * This function erase flash memory page before writing.
80  *
81  * This function is only use internally in this module.
82  */
83 static void flash_avr_flush(Flash *fd)
84 {
85         if (fd->page_dirty)
86         {
87
88                 LOG_INFO("Flushing page %d\n", fd->curr_page);
89
90                 // Wait while the SPM instruction is busy.
91                 boot_spm_busy_wait();
92
93                 LOG_INFO("Filling temparary page buffer...");
94
95                 // Fill the temporary buffer of the AVR
96                 for (page_addr_t page_addr = 0; page_addr < SPM_PAGESIZE; page_addr += 2)
97                 {
98                         uint16_t word = ((uint16_t)fd->page_buf[page_addr + 1] << 8) | fd->page_buf[page_addr];
99
100                         ATOMIC(boot_page_fill(page_addr, word));
101                 }
102                 LOG_INFO("Done.\n");
103
104                 wdt_reset();
105
106                 LOG_INFO("Erasing page, addr %u...", fd->curr_page * SPM_PAGESIZE);
107
108                 /* Page erase */
109                 ATOMIC(boot_page_erase(fd->curr_page * SPM_PAGESIZE));
110
111                 /* Wait until the memory is erased. */
112                 boot_spm_busy_wait();
113
114                 LOG_INFO("Done.\n");
115                 LOG_INFO("Writing page, addr %u...", fd->curr_page * SPM_PAGESIZE);
116
117                 /* Store buffer in flash page. */
118                 ATOMIC(boot_page_write(fd->curr_page * SPM_PAGESIZE));
119                 boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
120
121                 /*
122                 * Reenable RWW-section again. We need this if we want to jump back
123                 * to the application after bootloading.
124                 */
125                 ATOMIC(boot_rww_enable());
126
127                 fd->page_dirty = false;
128                 LOG_INFO("Done.\n");
129         }
130 }
131
132
133 /**
134  * Flush avr flash function.
135  *
136  * Write current buffered page in flash memory (if modified).
137  * This function erase flash memory page before writing.
138  */
139 static int flash_avr_kfileFlush(struct KFile *_fd)
140 {
141         Flash *fd = FLASH_CAST(_fd);
142         flash_avr_flush(fd);
143         return 0;
144 }
145
146
147 /**
148  * Check current page and if \a page is different, load it in
149  * temporary buffer.
150  */
151 static void flash_avr_loadPage(Flash *fd, page_t page)
152 {
153         if (page != fd->curr_page)
154         {
155                 flash_avr_flush(fd);
156                 // Load page
157                 memcpy_P(fd->page_buf, (const char *)(page * SPM_PAGESIZE), SPM_PAGESIZE);
158                 fd->curr_page = page;
159                 LOG_INFO("Loaded page %d\n", fd->curr_page);
160         }
161 }
162
163 /**
164  * Write program memory.
165  * Write \a size bytes from buffer \a _buf to file \a fd
166  * \note Write operations are buffered.
167  */
168 static size_t flash_avr_write(struct KFile *_fd, const void *_buf, size_t size)
169 {
170         Flash *fd = FLASH_CAST(_fd);
171         const uint8_t *buf =(const uint8_t *)_buf;
172
173         page_t page;
174         page_addr_t page_addr;
175         size_t total_write = 0;
176
177
178         ASSERT(fd->fd.seek_pos + (kfile_off_t)size <= (kfile_off_t)fd->fd.size);
179         size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
180
181         LOG_INFO("Writing at pos[%u]\n", fd->fd.seek_pos);
182         while (size)
183         {
184                 page = fd->fd.seek_pos / SPM_PAGESIZE;
185                 page_addr = fd->fd.seek_pos % SPM_PAGESIZE;
186
187                 flash_avr_loadPage(fd, page);
188
189                 size_t wr_len = MIN(size, SPM_PAGESIZE - page_addr);
190                 memcpy(fd->page_buf + page_addr, buf, wr_len);
191                 fd->page_dirty = true;
192
193                 buf += wr_len;
194                 fd->fd.seek_pos += wr_len;
195                 size -= wr_len;
196                 total_write += wr_len;
197         }
198         LOG_INFO("written %u bytes\n", total_write);
199         return total_write;
200 }
201
202 /**
203  * Open flash file \a fd
204  * \a name and \a mode are unused, cause flash memory is
205  * threated like one file.
206  */
207 static void flash_avr_open(struct Flash *fd)
208 {
209         fd->curr_page = 0;
210         memcpy_P(fd->page_buf, (const char *)(fd->curr_page * SPM_PAGESIZE), SPM_PAGESIZE);
211
212         fd->fd.seek_pos = 0;
213         fd->fd.size = (uint16_t)(FLASHEND - CONFIG_FLASH_AVR_BOOTSIZE + 1);
214         fd->page_dirty = false;
215
216         LOG_INFO("Flash file opened\n");
217 }
218
219 /**
220  * Close file \a fd
221  */
222 static int flash_avr_close(struct KFile *_fd)
223 {
224         Flash *fd = FLASH_CAST(_fd);
225         flash_avr_flush(fd);
226         LOG_INFO("Flash file closed\n");
227         return 0;
228 }
229
230 /**
231  * Reopen file \a fd
232  */
233 static struct KFile *flash_avr_reopen(struct KFile *fd)
234 {
235         Flash *_fd = FLASH_CAST(fd);
236         flash_avr_close(fd);
237         flash_avr_open((struct Flash *)_fd);
238         return fd;
239 }
240
241
242 /**
243  * Read from file \a fd \a size bytes and put it in buffer \a buf
244  * \return the number of bytes read.
245  */
246 static size_t flash_avr_read(struct KFile *_fd, void *buf, size_t size)
247 {
248         Flash *fd = FLASH_CAST(_fd);
249         ASSERT(fd->fd.seek_pos + (kfile_off_t)size <= (kfile_off_t)fd->fd.size);
250         size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
251
252         LOG_INFO("Reading at pos[%u]\n", fd->fd.seek_pos);
253         // Flush current buffered page (if modified).
254         flash_avr_flush(fd);
255
256         /*
257          * AVR pointers are 16 bits wide, this hack is needed to avoid
258          * compiler warning, cause fd->seek_pos is a 32bit offset.
259          */
260         const uint8_t *pgm_addr = (const uint8_t *)0;
261         pgm_addr += fd->fd.seek_pos;
262
263         memcpy_P(buf, pgm_addr, size);
264         fd->fd.seek_pos += size;
265         LOG_INFO("Read %u bytes\n", size);
266         return size;
267 }
268
269 /**
270  * Init AVR flash read/write file.
271  */
272 void flash_hw_init(struct Flash *fd)
273 {
274         memset(fd, 0, sizeof(*fd));
275         DB(fd->fd._type = KFT_FLASH);
276
277         // Set up flash programming functions.
278         fd->fd.reopen = flash_avr_reopen;
279         fd->fd.close = flash_avr_close;
280         fd->fd.read = flash_avr_read;
281         fd->fd.write = flash_avr_write;
282         fd->fd.seek = kfile_genericSeek;
283         fd->fd.flush = flash_avr_kfileFlush;
284         fd->curr_page = 0;
285
286         flash_avr_open(fd);
287 }
288
289