altos/lpc: Add bits for building flash loaders
[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; 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_flash.h>
20
21 #define IAP_LOCATION 0x1fff1ff1
22
23 typedef void (*iap_func)(uint32_t *in, uint32_t *out);
24
25 static void
26 iap(uint32_t *in, uint32_t *out)
27 {
28         ao_arch_block_interrupts();
29         lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_FLASHREG);
30         ((iap_func) IAP_LOCATION)(in, out);
31         ao_arch_release_interrupts();
32 }
33
34 #define LPC_IAP_PREPARE_WRITE           50
35 #define LPC_IAP_COPY_RAM_TO_FLASH       51
36 #define LPC_IAP_ERASE_SECTOR            52
37 #define LPC_IAP_BLANK_CHECK             53
38 #define LPC_IAP_READ_PART_ID            54
39 #define LPC_IAP_READ_BOOT_CODE_VERSION  55
40 #define LPC_IAP_COMPARE                 56
41 #define LPC_IAP_REINVOKE_ISP            57
42 #define LPC_IAP_READ_UID                58
43 #define LPC_IAP_ERASE_PAGE              59
44 #define LPC_IAP_EEPROM_WRITE            61
45 #define LPC_IAP_EEPROM_READ             62
46
47 #define LPC_IAP_CMD_SUCCESS             0
48 #define LPC_IAP_INVALID_COMMAND         1
49 #define LPC_IAP_SRC_ADDR_ERROR          2
50 #define LPC_IAP_DST_ADDR_ERROR          3
51 #define LPC_IAP_SRC_ADDR_NOT_MAPPED     4
52 #define LPC_IAP_DST_ADDR_NOT_MAPPED     5
53 #define LPC_IAP_COUNT_ERROR             6
54 #define LPC_IAP_INVALID_SECTOR          7
55 #define LPC_IAP_SECTOR_NOT_BLANK        8
56 #define LPC_IAP_SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION 9
57 #define LPC_IAP_COMPARE_ERROR           10
58 #define LPC_IAP_BUSY                    11
59 #define LPC_IAP_PARAM_ERROR             12
60 #define LPC_IAP_ADDR_ERROR              13
61 #define LPC_IAP_ADDR_NOT_MAPPED         14
62 #define LPC_IAP_CMD_LOCKED              15
63 #define LPC_IAP_INVALID_CODE            16
64 #define LPC_IAP_INVALID_BAUD_RATE       17
65 #define LPC_IAP_INVALID_STOP_BIT        18
66 #define LPC_IAP_CODE_READ_PROTECTION_ENABLED    19
67
68 #define LPC_FLASH_BASE                  ((uint8_t *) 0x0)
69 #define LPC_FLASH_SECTOR                4096
70 #define LPC_FLASH_SECTOR_MASK           (LPC_FLASH_SECTOR - 1)
71 #define LPC_FLASH_SECTOR_SHIFT          12
72
73 static uint32_t iap_in[5], iap_out[5];
74
75 static uint32_t
76 ao_lpc_addr_to_sector(uint8_t *addr)
77 {
78         uint32_t        off = addr - LPC_FLASH_BASE;
79
80         return off >> LPC_FLASH_SECTOR_SHIFT;
81 }
82
83 static uint8_t
84 ao_lpc_addr_is_sector_aligned(uint8_t *addr)
85 {
86         uint32_t        off = addr - LPC_FLASH_BASE;
87         return          (off & LPC_FLASH_SECTOR_MASK) == 0;
88 }
89
90 static uint32_t
91 ao_lpc_prepare_write(uint32_t start_sector, uint32_t end_sector)
92 {
93         iap_in[0] = LPC_IAP_PREPARE_WRITE;
94         iap_in[1] = start_sector;
95         iap_in[2] = end_sector;
96         iap(iap_in,iap_out);
97         return iap_out[0];
98 }
99
100 static uint32_t
101 ao_lpc_copy_ram_to_flash(uint8_t *dst, uint8_t *src, uint32_t len, uint32_t freq)
102 {
103         iap_in[0] = LPC_IAP_COPY_RAM_TO_FLASH;
104         iap_in[1] = (uint32_t) dst;
105         iap_in[2] = (uint32_t) src;
106         iap_in[3] = len;
107         iap_in[4] = freq;
108         iap(iap_in,iap_out);
109         return iap_out[0];
110 }
111
112 static uint32_t
113 ao_lpc_erase_sector(uint32_t start_sector, uint32_t end_sector, uint32_t freq)
114 {
115         iap_in[0] = LPC_IAP_ERASE_SECTOR;
116         iap_in[1] = start_sector;
117         iap_in[2] = end_sector;
118         iap_in[3] = freq;
119         iap(iap_in,iap_out);
120         return iap_out[0];
121 }
122
123 uint32_t
124 ao_lpc_read_part_id(void)
125 {
126         iap_in[0] = LPC_IAP_READ_PART_ID;
127         iap(iap_in,iap_out);
128         return iap_out[1];
129 }
130
131 uint32_t
132 ao_flash_erase_page(uint8_t *page)
133 {
134         uint32_t        ret = LPC_IAP_CMD_SUCCESS;
135         if (ao_lpc_addr_is_sector_aligned(page)) {
136                 uint32_t        sector = ao_lpc_addr_to_sector(page);
137                 ret = ao_lpc_prepare_write(sector, sector);
138                 if (ret == LPC_IAP_CMD_SUCCESS)
139                         ret = ao_lpc_erase_sector(sector, sector, AO_LPC_SYSCLK / 1000);
140         }
141         return ret;
142 }
143
144 uint32_t
145 ao_flash_page(uint8_t *page, uint8_t *src)
146 {
147         uint32_t        sector = ao_lpc_addr_to_sector(page);
148         uint32_t        ret;
149
150         ret = ao_flash_erase_page(page);
151         if (ret != LPC_IAP_CMD_SUCCESS)
152                 return ret;
153         ret = ao_lpc_prepare_write(sector, sector);
154         if (ret != LPC_IAP_CMD_SUCCESS)
155                 return ret;
156         ret = ao_lpc_copy_ram_to_flash(page, src, 256, AO_LPC_SYSCLK / 1000);
157         return ret;
158 }