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