3222818e3fe3d2313d7ffb112879164adec583d6
[bertos.git] / mware / prog_avr.c
1 /**
2  * \file
3  * <!--
4  * Copyright 2004 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  */
13
14 #include "prog.h"
15 #include <drv/wdt.h>
16 #include <cfg/macros.h> // MIN()
17 #include <cfg/compiler.h>
18 #include <flash.h>
19 #include <defines.h>
20 #include <avr/io.h>
21 #include <algos/rotating_hash.h>
22
23 #define PAGEBUF 512
24
25 typedef uint16_t page_addr_t;
26 typedef uint16_t page_t;
27
28 /**
29  * Temporary buffer for cointain data block to
30  * write on flash.
31  */
32 static uint8_t page_buf[PAGEBUF];
33
34 static page_t curr_pag_num = 0;
35
36
37
38 /**
39  * Erase Flash.
40  */
41 static void prog_erase_flash(void)
42 {
43         uint32_t flash_addr;
44
45         /* Erase the flash ROM */
46         #ifdef LARGE_MEMORY
47                 /*
48                  * SPM uses Z pointer but the pointer is only 16 bit and
49                  * can only address up to 64Kbytes FLASH. Higher locations
50                  * require the use of RAMPZ
51                  */
52                 RAMPZ = 0x00;
53
54                 for (flash_addr = 0; (flash_addr < (uint16_t)(APP_END & 0xFFFF)) | (RAMPZ == 0x00);
55                 {
56                         wdt_reset();
57
58                         /* Page erase */
59                         write_page(flash_addr, BV(PGERS) + BV(SPMEN));
60
61                         /* Re-enable the RWW section */
62                         write_page(flash_addr, BV(REENABLE_RWW_BIT) + BV(SPMEN));
63
64                         /* Last section on lower 64k segment is erased */
65                         if(flashgg_addr >= (0xFFFF - PAGESIZE))
66
67                                 /* RAMPZ has to be incremented into upper 64k segment */
68                                 RAMPZ = BV(RAMPZ0);
69                 }
70                 RAMPZ = 0x00;
71         #else /* LARGE_MEMORY */
72                  /* Application section = 60 pages */
73                 for (flash_addr = 0; flash_addr < APP_END; flash_addr += PAGESIZE)
74                 {
75                         wdt_reset();
76
77                         /* Page erase */
78                         write_page(flash_addr, BV(PGERS) + BV(SPMEN));
79                         /* Re-enable RWW section */
80                         write_page(flash_addr, BV(REENABLE_RWW_BIT) + BV(SPMEN));
81                 }
82         #endif /* LARGE_MEMORY */
83 }
84
85
86 /**
87  * Write a page in program memory.
88  */
89 static void prog_pagewrite(uint16_t addr)
90 {
91         write_page(addr, BV(PGWRT) + BV(SPMEN));
92
93         /* Re-enable the RWW section */
94         write_page(addr, BV(REENABLE_RWW_BIT) + BV(SPMEN));
95 }
96
97
98 /**
99  * Write program memory.
100  */
101 size_t  prog_write(struct _KFile *fd, const char *buf, size_t size)
102 {
103
104         page_t page;
105         page_addr_t page_addr;
106         size_t total_write = 0;
107         
108         while (size)
109         {
110                 page = fd->SeekPos / PAGEBUF;
111                 page_addr = fd->SeekPos % PAGEBUF;
112         
113                 prog_loadPage(page);
114         
115                 size_t wr_len = MIN(size, PAGEBUF - page_addr);
116                 memcpy(page_buf + page_addr, buf, wr_len);
117
118                 buf += wr_len;
119                 fd->SeekPos += wr_len;
120                 size -= wr_len;
121                 total_write += wr_len;
122         }
123         return total_write;
124 }
125
126 void prog_flush(void)
127 {
128                 
129         /* Fill the temporary buffer of the AVR */
130         for (page_addr_t page_addr = 0; page_addr < PAGEBUF; page_addr += 2)
131                 fill_temp_buffer(page_buf[page_addr + 1] | (uint16_t)page_buf[page_addr] << 8, page_addr);
132         
133
134         wdt_reset();
135
136         /* Page write */
137         prog_pagewrite(curr_page_num * PAGEBUF);
138 }
139
140 void prog_loadPage(page_t page)
141 {
142         if (page != curr_page_num)
143         {
144                 prog_flush();
145                 // Load page
146                 memcpy_P(page_buf, (const char *)(page * PAGEBUF), PAGEBUF);
147                 curr_page_num = page;
148         }
149 }