cc1111: Wait for internal flash write to complete
[fw/altos] / src / cc1111 / ao_intflash.c
1 /*
2  * Copyright © 2011  Anthony Towns <aj@erisian.com.au>
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 "cc1111.h"
20
21 #define ENDOFCODE  (CODESIZE)
22 #define AO_INTFLASH_BLOCK       1024
23 #define AO_INTFLASH_BLOCKS      ((0x8000 - ENDOFCODE)/AO_INTFLASH_BLOCK)
24 #define AO_INTFLASH_SIZE        (AO_INTFLASH_BLOCK * AO_INTFLASH_BLOCKS)
25 #define AO_INTFLASH_LOCATION    (0x8000 - AO_INTFLASH_SIZE)
26
27 /*
28  *       21000 * 24e6
29  * FWT = ------------
30  *           16e9
31  *
32  *     = 31.5
33  *
34  * Round up and use 32
35  */
36
37 #define FLASH_TIMING    0x20
38
39 #if AO_INTFLASH_BLOCKS < 2
40 #error "Too few pages"
41 #endif
42
43 #if AO_INFTLASH_LOCATION % 1024 != 0
44 #error "Pages aren't aligned properly"
45 #endif
46
47 __xdata __at(AO_INTFLASH_LOCATION) uint8_t ao_intflash[AO_INTFLASH_SIZE];
48
49 /* Total bytes of available storage */
50 __pdata uint32_t        ao_storage_total = sizeof(ao_intflash);
51
52 /* Block size - device is erased in these units. */
53 __pdata uint32_t        ao_storage_block = AO_INTFLASH_BLOCK;
54
55 /* Byte offset of config block. Will be ao_storage_block bytes long */
56 __pdata uint32_t        ao_storage_config = sizeof(ao_intflash) - AO_INTFLASH_BLOCK;
57
58 /* Storage unit size - device reads and writes must be within blocks of this size. */
59 __pdata uint16_t        ao_storage_unit = AO_INTFLASH_BLOCK;
60
61 __xdata static uint8_t  ao_intflash_dma_done;
62 static uint8_t ao_intflash_dma;
63
64 /*
65  * The internal flash chip is arranged in 1kB sectors; the
66  * chip cannot erase in units smaller than that.
67  *
68  * Writing happens in units of 2 bytes and
69  * can only change bits from 1 to 0. So, you can rewrite
70  * the same contents, or append to an existing page easily enough
71  */
72
73 /*
74  * Erase the specified sector
75  */
76 uint8_t
77 ao_storage_erase(uint32_t pos) __reentrant
78 {
79         uint16_t addr;
80
81         if (pos >= ao_storage_total || pos + ao_storage_block > ao_storage_total)
82                 return 0;
83
84         addr = ((uint16_t)(ao_intflash + pos) >> 1);
85
86         FADDRH = addr >> 8;
87         FADDRL = addr;
88
89         __critical {
90                 _asm
91                 .even
92                 orl _FCTL, #FCTL_ERASE;         ; FCTL |=  FCTL_ERASE
93                 nop                             ; Required, see datasheet.
94                 _endasm;
95         }
96
97         return 1;
98 }
99
100 /*
101  * Write to flash
102  */
103
104 static void
105 ao_intflash_write_aligned(uint16_t pos, __xdata void *d, uint16_t len) __reentrant
106 {
107         pos = ((uint16_t) ao_intflash + pos) >> 1;
108
109         ao_dma_set_transfer(ao_intflash_dma,
110                             d,
111                             &FWDATAXADDR,
112                             len,
113                             DMA_CFG0_WORDSIZE_8 |
114                             DMA_CFG0_TMODE_SINGLE |
115                             DMA_CFG0_TRIGGER_FLASH,
116                             DMA_CFG1_SRCINC_1 |
117                             DMA_CFG1_DESTINC_0 |
118                             DMA_CFG1_PRIORITY_HIGH);
119
120         FADDRH = pos >> 8;
121         FADDRL = pos;
122
123         ao_dma_start(ao_intflash_dma);
124
125         __critical {
126                 _asm
127                 .even
128                 orl _FCTL, #FCTL_WRITE;         ; FCTL |=  FCTL_WRITE
129                 nop
130                 _endasm;
131         }
132         __critical while (!ao_intflash_dma_done)
133                            ao_sleep(&ao_intflash_dma_done);
134 }
135
136 static void
137 ao_intflash_write_byte(uint16_t pos, uint8_t byte) __reentrant
138 {
139         static __xdata uint8_t b[2];
140
141         if (pos & 1) {
142                 b[0] = 0xff;
143                 b[1] = byte;
144         } else {
145                 b[0] = byte;
146                 b[1] = 0xff;
147         }
148         ao_intflash_write_aligned(pos, b, 2);
149 }
150
151 uint8_t
152 ao_storage_device_write(uint32_t pos32, __xdata void *v, uint16_t len) __reentrant
153 {
154         uint16_t pos = pos32;
155         __xdata uint8_t *d = v;
156         uint8_t oddlen;
157
158         if (pos >= ao_storage_total || pos + len > ao_storage_total)
159                 return 0;
160         if (len == 0)
161                 return 1;
162
163         if (pos & 1) {
164                 ao_intflash_write_byte(pos++, *d++);
165                 len--;
166         }
167         oddlen = len & 1;
168         len -= oddlen;
169         if (len)
170                 ao_intflash_write_aligned(pos, d, len);
171         if (oddlen)
172                 ao_intflash_write_byte(pos + len, d[len]);
173
174         return 1;
175 }
176
177 /*
178  * Read from flash
179  */
180 uint8_t
181 ao_storage_device_read(uint32_t pos, __xdata void *d, uint16_t len) __reentrant
182 {
183         if (pos >= ao_storage_total || pos + len > ao_storage_total)
184                 return 0;
185         ao_xmemcpy(d, ao_intflash+pos, len);
186         return 1;
187 }
188
189 void
190 ao_storage_flush(void) __reentrant
191 {
192 }
193
194 void
195 ao_storage_setup(void)
196 {
197 }
198
199 void
200 ao_storage_device_info(void) __reentrant
201 {
202         printf ("Using internal flash, starting at 0x%04x\n", AO_INTFLASH_LOCATION);
203 }
204
205 void
206 ao_storage_device_init(void)
207 {
208         ao_intflash_dma = ao_dma_alloc(&ao_intflash_dma_done);
209
210         FWT = FLASH_TIMING;
211 }