altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / src / lpc / ao_flash_lpc.c
1 /*
2  * Copyright © 2013 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_flash.h>
21
22 #define IAP_LOCATION 0x1fff1ff1
23
24 typedef void (*iap_func)(uint32_t *in, uint32_t *out);
25
26 static void
27 iap(uint32_t *in, uint32_t *out)
28 {
29         ao_arch_block_interrupts();
30         lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_FLASHREG);
31         ((iap_func) IAP_LOCATION)(in, out);
32         ao_arch_release_interrupts();
33 }
34
35 #define LPC_IAP_PREPARE_WRITE           50
36 #define LPC_IAP_COPY_RAM_TO_FLASH       51
37 #define LPC_IAP_ERASE_SECTOR            52
38 #define LPC_IAP_BLANK_CHECK             53
39 #define LPC_IAP_READ_PART_ID            54
40 #define LPC_IAP_READ_BOOT_CODE_VERSION  55
41 #define LPC_IAP_COMPARE                 56
42 #define LPC_IAP_REINVOKE_ISP            57
43 #define LPC_IAP_READ_UID                58
44 #define LPC_IAP_ERASE_PAGE              59
45 #define LPC_IAP_EEPROM_WRITE            61
46 #define LPC_IAP_EEPROM_READ             62
47
48 #define LPC_IAP_CMD_SUCCESS             0
49 #define LPC_IAP_INVALID_COMMAND         1
50 #define LPC_IAP_SRC_ADDR_ERROR          2
51 #define LPC_IAP_DST_ADDR_ERROR          3
52 #define LPC_IAP_SRC_ADDR_NOT_MAPPED     4
53 #define LPC_IAP_DST_ADDR_NOT_MAPPED     5
54 #define LPC_IAP_COUNT_ERROR             6
55 #define LPC_IAP_INVALID_SECTOR          7
56 #define LPC_IAP_SECTOR_NOT_BLANK        8
57 #define LPC_IAP_SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION 9
58 #define LPC_IAP_COMPARE_ERROR           10
59 #define LPC_IAP_BUSY                    11
60 #define LPC_IAP_PARAM_ERROR             12
61 #define LPC_IAP_ADDR_ERROR              13
62 #define LPC_IAP_ADDR_NOT_MAPPED         14
63 #define LPC_IAP_CMD_LOCKED              15
64 #define LPC_IAP_INVALID_CODE            16
65 #define LPC_IAP_INVALID_BAUD_RATE       17
66 #define LPC_IAP_INVALID_STOP_BIT        18
67 #define LPC_IAP_CODE_READ_PROTECTION_ENABLED    19
68
69 #define LPC_FLASH_BASE                  ((uint8_t *) 0x0)
70 #define LPC_FLASH_SECTOR                4096
71 #define LPC_FLASH_SECTOR_MASK           (LPC_FLASH_SECTOR - 1)
72 #define LPC_FLASH_SECTOR_SHIFT          12
73
74 static uint32_t iap_in[5], iap_out[5];
75
76 #if IS_FLASH_LOADER
77
78 static uint32_t
79 ao_lpc_addr_to_sector(uint8_t *addr)
80 {
81         uint32_t        off = (uint32_t) (addr - LPC_FLASH_BASE);
82
83         return off >> LPC_FLASH_SECTOR_SHIFT;
84 }
85
86 static uint8_t
87 ao_lpc_addr_is_sector_aligned(uint8_t *addr)
88 {
89         uint32_t        off = (uint32_t) (addr - LPC_FLASH_BASE);
90         return          (off & LPC_FLASH_SECTOR_MASK) == 0;
91 }
92
93 static uint32_t
94 ao_lpc_prepare_write(uint32_t start_sector, uint32_t end_sector)
95 {
96         iap_in[0] = LPC_IAP_PREPARE_WRITE;
97         iap_in[1] = start_sector;
98         iap_in[2] = end_sector;
99         iap(iap_in,iap_out);
100         return iap_out[0];
101 }
102
103 static uint32_t
104 ao_lpc_copy_ram_to_flash(uint8_t *dst, uint8_t *src, uint32_t len, uint32_t freq)
105 {
106         iap_in[0] = LPC_IAP_COPY_RAM_TO_FLASH;
107         iap_in[1] = (uint32_t) dst;
108         iap_in[2] = (uint32_t) src;
109         iap_in[3] = len;
110         iap_in[4] = freq;
111         iap(iap_in,iap_out);
112         return iap_out[0];
113 }
114
115 static uint32_t
116 ao_lpc_erase_sector(uint32_t start_sector, uint32_t end_sector, uint32_t freq)
117 {
118         iap_in[0] = LPC_IAP_ERASE_SECTOR;
119         iap_in[1] = start_sector;
120         iap_in[2] = end_sector;
121         iap_in[3] = freq;
122         iap(iap_in,iap_out);
123         return iap_out[0];
124 }
125
126 uint32_t
127 ao_lpc_read_part_id(void)
128 {
129         iap_in[0] = LPC_IAP_READ_PART_ID;
130         iap(iap_in,iap_out);
131         return iap_out[1];
132 }
133
134 uint32_t
135 ao_flash_erase_page(uint8_t *page)
136 {
137         uint32_t        ret = LPC_IAP_CMD_SUCCESS;
138         if (ao_lpc_addr_is_sector_aligned(page)) {
139                 uint32_t        sector = ao_lpc_addr_to_sector(page);
140                 ret = ao_lpc_prepare_write(sector, sector);
141                 if (ret == LPC_IAP_CMD_SUCCESS)
142                         ret = ao_lpc_erase_sector(sector, sector, AO_LPC_SYSCLK / 1000);
143         }
144         return ret;
145 }
146
147 uint32_t
148 ao_flash_page(uint8_t *page, uint8_t *src)
149 {
150         uint32_t        sector = ao_lpc_addr_to_sector(page);
151         uint32_t        ret;
152
153         ret = ao_flash_erase_page(page);
154         if (ret != LPC_IAP_CMD_SUCCESS)
155                 return ret;
156         ret = ao_lpc_prepare_write(sector, sector);
157         if (ret != LPC_IAP_CMD_SUCCESS)
158                 return ret;
159         ret = ao_lpc_copy_ram_to_flash(page, src, 256, AO_LPC_SYSCLK / 1000);
160         return ret;
161 }
162
163 #endif
164
165 #if LPC_EEPROM_BYTES
166
167 /*
168  * Write to eeprom
169  */
170
171 uint8_t
172 ao_eeprom_write(ao_pos_t pos, void *v, uint16_t len)
173 {
174         iap_in[0] = LPC_IAP_EEPROM_WRITE;
175         iap_in[1] = (uint32_t) pos;
176         iap_in[2] = (uint32_t) v;
177         iap_in[3] = (uint32_t) len;
178         iap_in[4] = AO_LPC_SYSCLK / 1000;
179         iap(iap_in,iap_out);
180         return iap_out[0] == LPC_IAP_CMD_SUCCESS;
181 }
182
183 /*
184  * Read from eeprom
185  */
186 uint8_t
187 ao_eeprom_read(ao_pos_t pos, void *v, uint16_t len)
188 {
189         iap_in[0] = LPC_IAP_EEPROM_READ;
190         iap_in[1] = (uint32_t) pos;
191         iap_in[2] = (uint32_t) v;
192         iap_in[3] = (uint32_t) len;
193         iap_in[4] = AO_LPC_SYSCLK / 1000;
194         iap(iap_in,iap_out);
195         return iap_out[0] == LPC_IAP_CMD_SUCCESS;
196 }
197
198 void
199 ao_eeprom_init(void)
200 {
201         /* Nothing to do here */
202 }
203 #endif