altos: Add ao_boot_chain to telemega v0.3
[fw/altos] / src / stm / ao_eeprom_stm.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_storage.h>
20
21 /* Total bytes of available storage */
22 ao_pos_t        ao_storage_total = 4096;
23
24 /* Block size - device is erased in these units. */
25 ao_pos_t        ao_storage_block = 1024;
26
27 /* Byte offset of config block. Will be ao_storage_block bytes long */
28 ao_pos_t        ao_storage_config = 0;
29
30 /* Storage unit size - device reads and writes must be within blocks of this size. */
31 uint16_t        ao_storage_unit = 1024;
32
33 /* Location of eeprom in address space */
34 #define stm_eeprom      ((uint8_t *) 0x08080000)
35
36 /*
37  * The internal flash chip is arranged in 8 byte sectors; the
38  * chip cannot erase in units smaller than that.
39  *
40  * Writing happens in units of 2 bytes and
41  * can only change bits from 1 to 0. So, you can rewrite
42  * the same contents, or append to an existing page easily enough
43  */
44
45 /*
46  * Erase the specified sector
47  */
48 uint8_t
49 ao_storage_erase(ao_pos_t pos) __reentrant
50 {
51         /* Not necessary */
52         return 1;
53 }
54
55 static void
56 ao_intflash_unlock(void)
57 {
58         /* Disable backup write protection */
59         stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
60
61         /* Unlock Data EEPROM and FLASH_PECR register */
62         stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY1;
63         stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY2;
64
65         if (stm_flash.pecr & (1 << STM_FLASH_PECR_PELOCK))
66                 printf ("eeprom unlock failed\n");
67
68         /* Configure the FTDW bit (FLASH_PECR[8]) to execute
69          * word write, whatever the previous value of the word
70          * being written to
71          */
72         stm_flash.pecr = ((0 << STM_FLASH_PECR_OBL_LAUNCH) |
73                           (0 << STM_FLASH_PECR_ERRIE) |
74                           (0 << STM_FLASH_PECR_EOPIE) |
75                           (0 << STM_FLASH_PECR_FPRG) |
76                           (0 << STM_FLASH_PECR_ERASE) |
77                           (1 << STM_FLASH_PECR_FTDW) |
78                           (0 << STM_FLASH_PECR_DATA) |
79                           (0 << STM_FLASH_PECR_PROG) |
80                           (0 << STM_FLASH_PECR_OPTLOCK) |
81                           (0 << STM_FLASH_PECR_PRGLOCK) |
82                           (0 << STM_FLASH_PECR_PELOCK));
83 }
84
85 static void
86 ao_intflash_lock(void)
87 {
88         stm_flash.pecr |= (1 << STM_FLASH_PECR_PELOCK);
89 }
90
91 static void
92 ao_intflash_wait(void)
93 {
94         /* Wait for the flash unit to go idle */
95         while (stm_flash.sr & (1 << STM_FLASH_SR_BSY))
96                 ;
97 }
98
99 static void
100 ao_intflash_write32(uint16_t pos, uint32_t w)
101 {
102         volatile uint32_t       *addr;
103
104         addr = (uint32_t *) (stm_eeprom + pos);
105
106         /* Write a word to a valid address in the data EEPROM */
107         *addr = w;
108         ao_intflash_wait();
109 }
110
111 static void
112 ao_intflash_write8(uint16_t pos, uint8_t d)
113 {
114         uint32_t        w, *addr, mask;
115         uint8_t         shift;
116         
117         addr = (uint32_t *) (stm_eeprom + (pos & ~3));
118
119         /* Compute word to be written */
120         shift = (pos & 3) << 3;
121         mask = 0xff << shift;
122         w = (*addr & ~mask) | (d << shift);
123
124         ao_intflash_write32(pos & ~3, w);
125 }
126
127 static uint8_t
128 ao_intflash_read(uint16_t pos)
129 {
130         return stm_eeprom[pos];
131 }
132
133 /*
134  * Write to flash
135  */
136
137 uint8_t
138 ao_storage_device_write(ao_pos_t pos32, __xdata void *v, uint16_t len) __reentrant
139 {
140         uint16_t pos = pos32;
141         __xdata uint8_t *d = v;
142
143         if (pos >= ao_storage_total || pos + len > ao_storage_total)
144                 return 0;
145
146         ao_intflash_unlock();
147         while (len) {
148                 if ((pos & 3) == 0 && len >= 4) {
149                         uint32_t        w;
150
151                         w = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
152                         ao_intflash_write32(pos, w);
153                         pos += 4;
154                         d += 4;
155                         len -= 4;
156                 } else {
157                         ao_intflash_write8(pos, *d);
158                         pos += 1;
159                         d += 1;
160                         len -= 1;
161                 }
162         }
163         ao_intflash_lock();
164
165         return 1;
166 }
167
168 /*
169  * Read from flash
170  */
171 uint8_t
172 ao_storage_device_read(ao_pos_t pos, __xdata void *v, uint16_t len) __reentrant
173 {
174         uint8_t *d = v;
175         
176         if (pos >= ao_storage_total || pos + len > ao_storage_total)
177                 return 0;
178         while (len--)
179                 *d++ = ao_intflash_read(pos++);
180         return 1;
181 }
182
183 void
184 ao_storage_flush(void) __reentrant
185 {
186 }
187
188 void
189 ao_storage_setup(void)
190 {
191 }
192
193 void
194 ao_storage_device_info(void) __reentrant
195 {
196         uint8_t i;
197         printf ("Using internal flash\n");
198 }
199
200 void
201 ao_storage_device_init(void)
202 {
203 }