altos: Split out SPI driver.
[fw/altos] / src / ao_flash.c
1 /*
2  * Copyright © 2009 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 "at45db161d.h"
20
21 #define FLASH_CS                P1_1
22 #define FLASH_CS_INDEX          1
23
24 __xdata uint8_t ao_flash_mutex;
25
26 #define ao_flash_delay() do { \
27         _asm nop _endasm; \
28         _asm nop _endasm; \
29         _asm nop _endasm; \
30 } while(0)
31
32 void ao_flash_cs_low(void)
33 {
34         ao_flash_delay();
35         FLASH_CS = 0;
36         ao_flash_delay();
37 }
38
39 void ao_flash_cs_high(void)
40 {
41         ao_flash_delay();
42         FLASH_CS = 1;
43         ao_flash_delay();
44 }
45
46 struct ao_flash_instruction {
47         uint8_t instruction;
48         uint8_t address[3];
49 } __xdata ao_flash_instruction;
50
51 static void
52 ao_flash_set_pagesize_512(void)
53 {
54         ao_flash_cs_low();
55         ao_flash_instruction.instruction = FLASH_SET_CONFIG;
56         ao_flash_instruction.address[0] = FLASH_SET_512_BYTE_0;
57         ao_flash_instruction.address[1] = FLASH_SET_512_BYTE_1;
58         ao_flash_instruction.address[2] = FLASH_SET_512_BYTE_2;
59         ao_spi_send(&ao_flash_instruction, 4);
60         ao_flash_cs_high();
61 }
62
63
64 static uint8_t
65 ao_flash_read_status(void)
66 {
67         ao_flash_cs_low();
68         ao_flash_instruction.instruction = FLASH_READ_STATUS;
69         ao_spi_send(&ao_flash_instruction, 1);
70         ao_spi_recv(&ao_flash_instruction, 1);
71         ao_flash_cs_high();
72         return ao_flash_instruction.instruction;
73 }
74
75 #define FLASH_BLOCK_NONE        0xffff
76
77 static __xdata uint8_t ao_flash_data[FLASH_BLOCK_SIZE_MAX];
78 static __pdata uint16_t ao_flash_block = FLASH_BLOCK_NONE;
79 static __pdata uint8_t  ao_flash_block_dirty;
80 static __pdata uint8_t  ao_flash_write_pending;
81 static __pdata uint8_t  ao_flash_setup_done;
82 static __data uint32_t  ao_flash_device_size;
83 static __data uint8_t   ao_flash_block_shift;
84 static __data uint16_t  ao_flash_block_size;
85
86 static void
87 ao_flash_setup(void)
88 {
89         uint8_t status;
90
91         if (ao_flash_setup_done)
92                 return;
93
94         ao_mutex_get(&ao_flash_mutex);
95         if (ao_flash_setup_done) {
96                 ao_mutex_put(&ao_flash_mutex);
97                 return;
98         }
99
100         /* On first use, check to see if the flash chip has
101          * been programmed to use 512 byte pages. If not, do so.
102          * And then, because the flash part must be power cycled
103          * for that change to take effect, panic.
104          */
105         status = ao_flash_read_status();
106
107         if (!(status & FLASH_STATUS_PAGESIZE_512)) {
108                 ao_flash_set_pagesize_512();
109                 ao_panic(AO_PANIC_FLASH);
110         }
111
112         switch (status & 0x3c) {
113
114         /* AT45DB321D */
115         case 0x34:
116                 ao_flash_block_shift = 9;
117                 ao_flash_device_size = ((uint32_t) 4 * (uint32_t) 1024 * (uint32_t) 1024);
118                 break;
119
120         /* AT45DB161D */
121         case 0x2c:
122                 ao_flash_block_shift = 9;
123                 ao_flash_device_size = ((uint32_t) 2 * (uint32_t) 1024 * (uint32_t) 1024);
124                 break;
125
126         /* AT45DB081D */
127         case 0x24:
128                 ao_flash_block_shift = 8;
129                 ao_flash_device_size = ((uint32_t) 1024 * (uint32_t) 1024);
130                 break;
131
132         /* AT45DB041D */
133         case 0x1c:
134                 ao_flash_block_shift = 8;
135                 ao_flash_device_size = ((uint32_t) 512 * (uint32_t) 1024);
136                 break;
137
138         /* AT45DB021D */
139         case 0x14:
140                 ao_flash_block_shift = 8;
141                 ao_flash_device_size = ((uint32_t) 256 * (uint32_t) 1024);
142                 break;
143
144         /* AT45DB011D */
145         case 0x0c:
146                 ao_flash_block_shift = 8;
147                 ao_flash_device_size = ((uint32_t) 128 * (uint32_t) 1024);
148                 break;
149
150         default:
151                 ao_panic(AO_PANIC_FLASH);
152         }
153         ao_flash_block_size = 1 << ao_flash_block_shift;
154         ao_flash_setup_done = 1;
155         ao_mutex_put(&ao_flash_mutex);
156 }
157
158 static void
159 ao_flash_wait_write(void)
160 {
161         if (ao_flash_write_pending) {
162                 for (;;) {
163                         uint8_t status = ao_flash_read_status();
164                         if ((status & FLASH_STATUS_RDY))
165                                 break;
166                 }
167                 ao_flash_write_pending = 0;
168         }
169 }
170
171 /* Write the current block to the FLASHPROM */
172 static void
173 ao_flash_write_block(void)
174 {
175         ao_flash_wait_write();
176         ao_flash_cs_low();
177         ao_flash_instruction.instruction = FLASH_WRITE;
178
179         /* 13/14 block bits + 9/8 byte bits (always 0) */
180         ao_flash_instruction.address[0] = ao_flash_block >> (16 - ao_flash_block_shift);
181         ao_flash_instruction.address[1] = ao_flash_block << (ao_flash_block_shift - 8);
182         ao_flash_instruction.address[2] = 0;
183         ao_spi_send(&ao_flash_instruction, 4);
184         ao_spi_send(ao_flash_data, FLASH_BLOCK_SIZE);
185         ao_flash_cs_high();
186         ao_flash_write_pending = 1;
187 }
188
189 /* Read the current block from the FLASHPROM */
190 static void
191 ao_flash_read_block(void)
192 {
193         ao_flash_wait_write();
194         ao_flash_cs_low();
195         ao_flash_instruction.instruction = FLASH_READ;
196
197         /* 13/14 block bits + 9/8 byte bits (always 0) */
198         ao_flash_instruction.address[0] = ao_flash_block >> (16 - ao_flash_block_shift);
199         ao_flash_instruction.address[1] = ao_flash_block << (ao_flash_block_shift - 8);
200         ao_flash_instruction.address[2] = 0;
201         ao_spi_send(&ao_flash_instruction, 4);
202         ao_spi_recv(ao_flash_data, FLASH_BLOCK_SIZE);
203         ao_flash_cs_high();
204 }
205
206 static void
207 ao_flash_flush_internal(void)
208 {
209         if (ao_flash_block_dirty) {
210                 ao_flash_write_block();
211                 ao_flash_block_dirty = 0;
212         }
213 }
214
215 static void
216 ao_flash_fill(uint16_t block)
217 {
218         if (block != ao_flash_block) {
219                 ao_flash_flush_internal();
220                 ao_flash_block = block;
221                 ao_flash_read_block();
222         }
223 }
224
225 uint8_t
226 ao_ee_write(uint32_t pos, uint8_t *buf, uint16_t len) __reentrant
227 {
228         uint16_t block;
229         uint16_t this_len;
230         uint16_t        this_off;
231
232         ao_flash_setup();
233         if (pos >= FLASH_DATA_SIZE || pos + len > FLASH_DATA_SIZE)
234                 return 0;
235         while (len) {
236
237                 /* Compute portion of transfer within
238                  * a single block
239                  */
240                 this_off = (uint16_t) pos & FLASH_BLOCK_MASK;
241                 this_len = FLASH_BLOCK_SIZE - this_off;
242                 block = (uint16_t) (pos >> FLASH_BLOCK_SHIFT);
243                 if (this_len > len)
244                         this_len = len;
245
246                 /* Transfer the data */
247                 ao_mutex_get(&ao_flash_mutex); {
248                         if (this_len != FLASH_BLOCK_SIZE)
249                                 ao_flash_fill(block);
250                         else {
251                                 ao_flash_flush_internal();
252                                 ao_flash_block = block;
253                         }
254                         memcpy(ao_flash_data + this_off, buf, this_len);
255                         ao_flash_block_dirty = 1;
256                 } ao_mutex_put(&ao_flash_mutex);
257
258                 /* See how much is left */
259                 buf += this_len;
260                 len -= this_len;
261                 pos += this_len;
262         }
263         return 1;
264 }
265
266 uint8_t
267 ao_ee_read(uint32_t pos, uint8_t *buf, uint16_t len) __reentrant
268 {
269         uint16_t block;
270         uint16_t this_len;
271         uint16_t this_off;
272
273         ao_flash_setup();
274         if (pos >= FLASH_DATA_SIZE || pos + len > FLASH_DATA_SIZE)
275                 return 0;
276         while (len) {
277
278
279                 /* Compute portion of transfer within
280                  * a single block
281                  */
282                 this_off = (uint16_t) pos & FLASH_BLOCK_MASK;
283                 this_len = FLASH_BLOCK_SIZE - this_off;
284                 block = (uint16_t) (pos >> FLASH_BLOCK_SHIFT);
285                 if (this_len > len)
286                         this_len = len;
287
288                 /* Transfer the data */
289                 ao_mutex_get(&ao_flash_mutex); {
290                         ao_flash_fill(block);
291                         memcpy(buf, ao_flash_data + this_off, this_len);
292                 } ao_mutex_put(&ao_flash_mutex);
293
294                 /* See how much is left */
295                 buf += this_len;
296                 len -= this_len;
297                 pos += this_len;
298         }
299         return 1;
300 }
301
302 void
303 ao_ee_flush(void) __reentrant
304 {
305         ao_mutex_get(&ao_flash_mutex); {
306                 ao_flash_flush_internal();
307         } ao_mutex_put(&ao_flash_mutex);
308 }
309
310 /*
311  * Read/write the config block, which is in
312  * the last block of the flash
313  */
314
315 uint8_t
316 ao_ee_write_config(uint8_t *buf, uint16_t len) __reentrant
317 {
318         ao_flash_setup();
319         if (len > FLASH_BLOCK_SIZE)
320                 return 0;
321         ao_mutex_get(&ao_flash_mutex); {
322                 ao_flash_fill(FLASH_CONFIG_BLOCK);
323                 memcpy(ao_flash_data, buf, len);
324                 ao_flash_block_dirty = 1;
325                 ao_flash_flush_internal();
326         } ao_mutex_put(&ao_flash_mutex);
327         return 1;
328 }
329
330 uint8_t
331 ao_ee_read_config(uint8_t *buf, uint16_t len) __reentrant
332 {
333         ao_flash_setup();
334         if (len > FLASH_BLOCK_SIZE)
335                 return 0;
336         ao_mutex_get(&ao_flash_mutex); {
337                 ao_flash_fill(FLASH_CONFIG_BLOCK);
338                 memcpy(buf, ao_flash_data, len);
339         } ao_mutex_put(&ao_flash_mutex);
340         return 1;
341 }
342
343 static void
344 flash_dump(void) __reentrant
345 {
346         uint8_t b;
347         uint16_t block;
348         uint8_t i;
349
350         ao_cmd_hex();
351         block = ao_cmd_lex_i;
352         if (ao_cmd_status != ao_cmd_success)
353                 return;
354         i = 0;
355         do {
356                 if ((i & 7) == 0) {
357                         if (i)
358                                 putchar('\n');
359                         ao_cmd_put16((uint16_t) i);
360                 }
361                 putchar(' ');
362                 ao_ee_read(((uint32_t) block << 8) | i, &b, 1);
363                 ao_cmd_put8(b);
364                 ++i;
365         } while (i != 0);
366         putchar('\n');
367 }
368
369 static void
370 flash_store(void) __reentrant
371 {
372         uint16_t block;
373         uint8_t i;
374         uint16_t len;
375         uint8_t b;
376         uint32_t addr;
377
378         ao_cmd_hex();
379         block = ao_cmd_lex_i;
380         ao_cmd_hex();
381         i = ao_cmd_lex_i;
382         addr = ((uint32_t) block << 8) | i;
383         ao_cmd_hex();
384         len = ao_cmd_lex_i;
385         if (ao_cmd_status != ao_cmd_success)
386                 return;
387         while (len--) {
388                 ao_cmd_hex();
389                 if (ao_cmd_status != ao_cmd_success)
390                         return;
391                 b = ao_cmd_lex_i;
392                 ao_ee_write(addr, &b, 1);
393                 addr++;
394         }
395         ao_ee_flush();
396 }
397
398 void
399 ao_ee_dump_config(void) __reentrant
400 {
401         uint16_t        i;
402         printf("Configuration block %d\n", FLASH_CONFIG_BLOCK);
403         ao_mutex_get(&ao_flash_mutex); {
404                 ao_flash_flush_internal();
405                 ao_flash_block = FLASH_BLOCK_NONE;
406                 ao_flash_fill(FLASH_CONFIG_BLOCK);
407                 i = 0;
408                 do {
409                         if ((i & 7) == 0) {
410                                 if (i)
411                                         putchar('\n');
412                                 ao_cmd_put16((uint16_t) i);
413                         }
414                         putchar(' ');
415                         ao_cmd_put8(ao_flash_data[i]);
416                         ++i;
417                 } while (i < sizeof (ao_config));
418         } ao_mutex_put(&ao_flash_mutex);
419 }
420
421 static void
422 flash_status(void) __reentrant
423 {
424         uint8_t status;
425
426         ao_flash_setup();
427         ao_mutex_get(&ao_flash_mutex); {
428                 status = ao_flash_read_status();
429                 printf ("Flash status: 0x%02x\n", status);
430                 printf ("Flash block shift: %d\n", FLASH_BLOCK_SHIFT);
431                 printf ("Flash block size: %d\n", FLASH_BLOCK_SIZE);
432                 printf ("Flash block mask: %d\n", FLASH_BLOCK_MASK);
433                 printf ("Flash device size: %ld\n", FLASH_DEVICE_SIZE);
434                 printf ("Flash data size: %ld\n", FLASH_DATA_SIZE);
435                 printf ("Flash config block: %d\n", FLASH_CONFIG_BLOCK);
436         } ao_mutex_put(&ao_flash_mutex);
437         ao_ee_dump_config();
438 }
439
440 __code struct ao_cmds ao_flash_cmds[] = {
441         { 'e', flash_dump,      "e <block>                          Dump a block of flash data" },
442         { 'w', flash_store,     "w <block> <start> <len> <data> ... Write data to flash" },
443         { 'f', flash_status,    "f                                  Show flash status register" },
444         { 0,   flash_store, NULL },
445 };
446
447 /*
448  * To initialize the chip, set up the CS line and
449  * the SPI interface
450  */
451 void
452 ao_ee_init(void)
453 {
454         /* set up CS */
455         FLASH_CS = 1;
456         P1DIR |= (1 << FLASH_CS_INDEX);
457         P1SEL &= ~(1 << FLASH_CS_INDEX);
458         ao_cmd_register(&ao_flash_cmds[0]);
459 }