Move mware/prog_avr module to drv/flash_avr; add kfile Ctor; merge flash read/write...
[bertos.git] / drv / flash_avr.c
1 /**
2  * \file
3  * <!--
4  * Copyright 2007 Develer S.r.l. (http://www.develer.com/)
5  * All Rights Reserved.
6  * -->
7  *
8  * \brief Self programming routines.
9  *
10  * \version $Id$
11  * \author Francesco Sacchi <batt@develer.com>
12  * \author Daniele Basile <asterix@develer.com>
13  *
14  * This module implements a kfile-like access for Atmel avr
15  * internal flash.
16  * Internal flash writing access is controlled by BOOTSZ fuses, check
17  * datasheet for details.
18  */
19
20 #include "flash_avr.h"
21
22 #include <string.h>
23
24 #include <drv/wdt.h>
25 #include <cfg/macros.h> // MIN()
26 #include <cfg/compiler.h>
27 #include <cfg/debug.h>
28
29 #include <avr/io.h>
30 #include <avr/boot.h>
31 #include <avr/pgmspace.h>
32
33 typedef uint16_t avr_page_addr_t;
34 typedef uint16_t avr_page_t;
35
36 /**
37  * Temporary buffer cointaing data block to
38  * write on flash.
39  */
40 static uint8_t page_buf[SPM_PAGESIZE];
41
42 bool page_modified; /// Flag for checking if current page is modified.
43
44 /**
45  * Current buffered page.
46  */
47 static avr_page_t curr_page = 0;
48
49 /**
50  * Write current buffered page in flash memory (if modified).
51  * This function erase flash memory page before writing.
52  */
53 static void avrflash_flush(void)
54 {
55         if (page_modified)
56         {
57                 kprintf("Flushing page %d\n", curr_page);
58
59                 boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
60
61                 kprintf("Filling temparary page buffer...");
62                 /* Fill the temporary buffer of the AVR */
63                 for (avr_page_addr_t page_addr = 0; page_addr < SPM_PAGESIZE; page_addr += 2)
64                 {
65                         uint16_t word = ((uint16_t)page_buf[page_addr + 1] << 8) | page_buf[page_addr];
66
67                         ATOMIC(boot_page_fill(page_addr, word));
68                 }
69                 kprintf("Done.\n");
70
71                 wdt_reset();
72
73                 kprintf("Erasing page, addr %d...", curr_page * SPM_PAGESIZE);
74
75                 /* Page erase */
76                 ATOMIC(boot_page_erase(curr_page * SPM_PAGESIZE));
77
78                 /* Wait until the memory is erased. */
79                 boot_spm_busy_wait();
80
81                 kprintf("Done.\n");
82                 kprintf("Writing page, addr %d...", curr_page * SPM_PAGESIZE);
83
84                 /* Store buffer in flash page. */
85                 ATOMIC(boot_page_write(curr_page * SPM_PAGESIZE));
86                 boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
87
88                 /*
89                 * Reenable RWW-section again. We need this if we want to jump back
90                 * to the application after bootloading.
91                 */
92                 ATOMIC(boot_rww_enable());
93
94                 page_modified = false;
95                 kprintf("Done.\n");
96         }
97 }
98
99
100 /**
101  * Check current page and if \a page is different, load it in
102  * temporary buffer.
103  */
104 static void avrflash_loadPage(avr_page_t page)
105 {
106         if (page != curr_page)
107         {
108                 avrflash_flush();
109                 // Load page
110                 memcpy_P(page_buf, (const char *)(page * SPM_PAGESIZE), SPM_PAGESIZE);
111                 curr_page = page;
112                 kprintf("Loaded page %d\n", curr_page);
113         }
114 }
115
116 /**
117  * Write program memory.
118  * Write \a size bytes from buffer \a _buf to file \a *fd
119  * \note Write operations are buffered.
120  */
121 static size_t avrflash_write(struct _KFile *fd, const void *_buf, size_t size)
122 {
123         const uint8_t *buf =(const uint8_t *)_buf;
124
125         avr_page_t page;
126         avr_page_addr_t page_addr;
127         size_t total_write = 0;
128
129         ASSERT(fd->seek_pos + size <= fd->size);
130         size = MIN((uint32_t)size, fd->size - fd->seek_pos);
131
132         kprintf("Writing at pos[%d]\n", fd->seek_pos);
133         while (size)
134         {
135                 page = fd->seek_pos / SPM_PAGESIZE;
136                 page_addr = fd->seek_pos % SPM_PAGESIZE;
137
138                 avrflash_loadPage(page);
139
140                 size_t wr_len = MIN(size, SPM_PAGESIZE - page_addr);
141                 memcpy(page_buf + page_addr, buf, wr_len);
142                 page_modified = true;
143
144                 buf += wr_len;
145                 fd->seek_pos += wr_len;
146                 size -= wr_len;
147                 total_write += wr_len;
148         }
149         kprintf("written %d bytes\n", total_write);
150         return total_write;
151 }
152
153 /**
154  * Open flash file \a *fd
155  * \a name and \a mode are unused, cause flash memory is
156  * threated like one file.
157  */
158 static bool avrflash_open(struct _KFile *fd, UNUSED_ARG(const char *, name), UNUSED_ARG(int, mode))
159 {
160         curr_page = 0;
161         memcpy_P(page_buf, (const char *)(curr_page * SPM_PAGESIZE), SPM_PAGESIZE);
162
163         fd->seek_pos = 0;
164         fd->size = (uint16_t)(FLASHEND - CONFIG_BOOT_SIZE + 1);
165         page_modified = false;
166
167         kprintf("Flash file opened\n");
168         return true;
169 }
170
171 /**
172  * Close file \a *fd
173  */
174 static bool avrflash_close(UNUSED_ARG(struct _KFile *,fd))
175 {
176         avrflash_flush();
177         kprintf("Flash file closed\n");
178         return true;
179 }
180
181 /**
182  * Move \a *fd file seek position of \a offset bytes
183  * from current position.
184  */
185 static bool avrflash_seek(struct _KFile *fd, int32_t offset)
186 {
187         ASSERT(fd->seek_pos + offset <= fd->size);
188
189         /* Bound check */
190         if (fd->seek_pos + offset > fd->size)
191                 return false;
192
193         fd->seek_pos += offset;
194         kprintf("Flash seek to [%d]\n", fd->seek_pos);
195
196         return true;
197 }
198
199 /**
200  * Read from file \a *fd \a size bytes and put it in buffer \a *buf
201  * \return the number of bytes read.
202  */
203 static size_t avrflash_read(struct _KFile *fd, void *buf, size_t size)
204 {
205         ASSERT(fd->seek_pos + size <= fd->size);
206         size = MIN((uint32_t)size, fd->size - fd->seek_pos);
207
208         kprintf("Reading at pos[%d]\n", fd->seek_pos);
209         // Flush current buffered page (if modified).
210         avrflash_flush();
211
212         /*
213          * AVR pointers are 16 bits wide, this hack is needed to avoid
214          * compiler warning, cause fd->seek_pos is a 32bit offset.
215          */
216         const uint8_t *pgm_addr = (const uint8_t *)0;
217         pgm_addr += fd->seek_pos;
218
219         memcpy_P(buf, pgm_addr, size);
220         fd->seek_pos += size;
221         kprintf("Read %d bytes\n", size);
222         return size;
223 }
224
225 /**
226  * Init AVR flash read/write file.
227  */
228 void avrflash_init(struct _KFile *fd)
229 {
230         // Set up flash programming functions.
231         fd.open = avrflash_open;
232         fd.close = avrflash_close;
233         fd.read = avrflash_read;
234         fd.write = avrflash_write;
235         fd.seek = avrflash_seek;
236 }
237
238 #if CONFIG_TEST
239
240 #define TEST_SIZE 683
241 #define ONE_BYTE_TEST_ADDRESS 347
242
243 uint8_t test_buf[TEST_SIZE];
244 uint8_t save_buf[TEST_SIZE];
245
246 /**
247  * Program memory read/write subtest.
248  * Try to write/read in the same \param f file location \param _size bytes.
249  * \return true if all is ok, false otherwise
250  * \note Restore file position at exit (if no error)
251  * \note Test buffer \param buf must be filled with
252  * the following statement:
253  * <pre>
254  * buf[i] = i & 0xff
255  * </pre>
256  */
257 static bool avrflash_rwTest(KFile *f, uint8_t *buf, size_t _size)
258 {
259         int32_t size = _size;
260         // Write test buffer
261         if (f->write(f, buf, size) != size)
262                 return false;
263         f->seek(f, -size);
264
265         // Reset test buffer
266         memset(buf, 0, size);
267
268         // Read flash in test buffer
269         if (f->read(f, buf, size) != size)
270                 return false;
271         f->seek(f, -size);
272
273         // Check test result
274         for (size_t i = 0; i < size; i++)
275                 if (buf[i] != (i & 0xff))
276                         return false;
277
278         return true;
279 }
280
281 /**
282  * Test for program memory read/write.
283  */
284 bool avrflash_test(void)
285 {
286         KFile fd;
287
288         // Set up flash programming functions.
289         avrflash_init(&fd);
290
291         // Fill in test buffer
292         for (int i = 0; i < TEST_SIZE; i++)
293                 test_buf[i] = (i & 0xff);
294
295         // Open flash
296         fd.open(&fd, NULL, 0);
297         // Save flash content (for later restore).
298         fd.read(&fd, save_buf, sizeof(save_buf));
299         fd.seek(&fd, -TEST_SIZE);
300
301         // Test flash read/write to address 0..TEST_SIZE
302         if (!avrflash_rwTest(&fd, test_buf, TEST_SIZE))
303                 goto avrflash_test_end;
304
305         // One byte read/write test
306         fd.seek(&fd, ONE_BYTE_TEST_ADDRESS); // Random address
307         if (!avrflash_rwTest(&fd, test_buf, 1))
308                 goto avrflash_test_end;
309         fd.seek(&fd, -(int32_t)ONE_BYTE_TEST_ADDRESS);
310
311         // Restore old flash data
312         if (fd.write(&fd, save_buf, sizeof(test_buf)) != TEST_SIZE)
313                 goto avrflash_test_end;
314         fd.seek(&fd, -TEST_SIZE);
315
316         // Go to the Flash end
317         fd.seek(&fd, fd.size - TEST_SIZE);
318         // Save flash content (for later restore).
319         fd.read(&fd, save_buf, sizeof(save_buf));
320         fd.seek(&fd, -TEST_SIZE);
321
322         // Test flash read/write to address (FLASHEND - TEST_SIZE) ... FLASHEND
323         if (!avrflash_rwTest(&fd, test_buf, TEST_SIZE))
324                 goto avrflash_test_end;
325
326         // Go to half test size.
327         fd.seek(&fd, (TEST_SIZE / 2));
328
329         // This test should FAIL, cause we try to write over file end.
330         if (avrflash_rwTest(&fd, test_buf, TEST_SIZE))
331                 goto avrflash_test_end;
332
333         fd.seek(&fd, -TEST_SIZE);
334         // Restore old flash data
335         fd.write(&fd, save_buf, TEST_SIZE);
336
337         fd.close(&fd);
338         return true;
339
340 avrflash_test_end:
341         fd.close(&fd);
342         return false;
343 }
344
345 #endif