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