4207a8607e3d593df3482505ad5ca42eb411d385
[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_eeprom.h>
20
21 /* Total bytes of available storage */
22 const ao_pos_t ao_eeprom_total = 4096;
23
24 /* Location of eeprom in address space */
25 #define stm_eeprom      ((uint8_t *) 0x08080000)
26
27 /*
28  * The internal flash chip is arranged in 8 byte sectors; the
29  * chip cannot erase in units smaller than that.
30  *
31  * Writing happens in units of 2 bytes and
32  * can only change bits from 1 to 0. So, you can rewrite
33  * the same contents, or append to an existing page easily enough
34  */
35
36 static void
37 ao_intflash_unlock(void)
38 {
39         /* Disable backup write protection */
40         stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
41
42         /* Unlock Data EEPROM and FLASH_PECR register */
43         stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY1;
44         stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY2;
45
46         if (stm_flash.pecr & (1 << STM_FLASH_PECR_PELOCK))
47                 printf ("eeprom unlock failed\n");
48
49         /* Configure the FTDW bit (FLASH_PECR[8]) to execute
50          * word write, whatever the previous value of the word
51          * being written to
52          */
53         stm_flash.pecr = ((0 << STM_FLASH_PECR_OBL_LAUNCH) |
54                           (0 << STM_FLASH_PECR_ERRIE) |
55                           (0 << STM_FLASH_PECR_EOPIE) |
56                           (0 << STM_FLASH_PECR_FPRG) |
57                           (0 << STM_FLASH_PECR_ERASE) |
58                           (1 << STM_FLASH_PECR_FTDW) |
59                           (0 << STM_FLASH_PECR_DATA) |
60                           (0 << STM_FLASH_PECR_PROG) |
61                           (0 << STM_FLASH_PECR_OPTLOCK) |
62                           (0 << STM_FLASH_PECR_PRGLOCK) |
63                           (0 << STM_FLASH_PECR_PELOCK));
64 }
65
66 static void
67 ao_intflash_lock(void)
68 {
69         stm_flash.pecr |= (1 << STM_FLASH_PECR_PELOCK);
70 }
71
72 static void
73 ao_intflash_wait(void)
74 {
75         /* Wait for the flash unit to go idle */
76         while (stm_flash.sr & (1 << STM_FLASH_SR_BSY))
77                 ;
78 }
79
80 static void
81 ao_intflash_write32(uint16_t pos, uint32_t w)
82 {
83         volatile uint32_t       *addr;
84
85         addr = (uint32_t *) (stm_eeprom + pos);
86
87         /* Write a word to a valid address in the data EEPROM */
88         *addr = w;
89         ao_intflash_wait();
90 }
91
92 static void
93 ao_intflash_write8(uint16_t pos, uint8_t d)
94 {
95         uint32_t        w, *addr, mask;
96         uint8_t         shift;
97         
98         addr = (uint32_t *) (stm_eeprom + (pos & ~3));
99
100         /* Compute word to be written */
101         shift = (pos & 3) << 3;
102         mask = 0xff << shift;
103         w = (*addr & ~mask) | (d << shift);
104
105         ao_intflash_write32(pos & ~3, w);
106 }
107
108 static uint8_t
109 ao_intflash_read(uint16_t pos)
110 {
111         return stm_eeprom[pos];
112 }
113
114 /*
115  * Write to eeprom
116  */
117
118 uint8_t
119 ao_eeprom_write(ao_pos_t pos32, __xdata void *v, uint16_t len)
120 {
121         uint16_t pos = pos32;
122         __xdata uint8_t *d = v;
123
124         if (pos >= ao_eeprom_total || pos + len > ao_eeprom_total)
125                 return 0;
126
127         ao_intflash_unlock();
128         while (len) {
129                 if ((pos & 3) == 0 && len >= 4) {
130                         uint32_t        w;
131
132                         w = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
133                         ao_intflash_write32(pos, w);
134                         pos += 4;
135                         d += 4;
136                         len -= 4;
137                 } else {
138                         ao_intflash_write8(pos, *d);
139                         pos += 1;
140                         d += 1;
141                         len -= 1;
142                 }
143         }
144         ao_intflash_lock();
145
146         return 1;
147 }
148
149 /*
150  * Read from eeprom
151  */
152 uint8_t
153 ao_eeprom_read(ao_pos_t pos, __xdata void *v, uint16_t len)
154 {
155         uint8_t *d = v;
156         
157         if (pos >= ao_eeprom_total || pos + len > ao_eeprom_total)
158                 return 0;
159         while (len--)
160                 *d++ = ao_intflash_read(pos++);
161         return 1;
162 }
163
164 /*
165  * Initialize eeprom
166  */
167
168 void
169 ao_eeprom_init(void)
170 {
171         /* Nothing to do here */
172 }