altos: Document what HAS_BOOT_RADIO does in the m25 driver
[fw/altos] / src / drivers / ao_m25.c
1 /*
2  * Copyright © 2010 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
20 /* Total bytes of available storage */
21 __pdata uint32_t        ao_storage_total;
22
23 /* Block size - device is erased in these units. At least 256 bytes */
24 __pdata uint32_t        ao_storage_block;
25
26 /* Byte offset of config block. Will be ao_storage_block bytes long */
27 __pdata uint32_t        ao_storage_config;
28
29 /* Storage unit size - device reads and writes must be within blocks of this size. Usually 256 bytes. */
30 __pdata uint16_t        ao_storage_unit;
31
32 /*
33  * Each flash chip is arranged in 64kB sectors; the
34  * chip cannot erase in units smaller than that.
35  *
36  * Writing happens in units of 256 byte pages and
37  * can only change bits from 1 to 0. So, you can rewrite
38  * the same contents, or append to an existing page easily enough
39  */
40
41 #define M25_WREN        0x06    /* Write Enable */
42 #define M25_WRDI        0x04    /* Write Disable */
43 #define M25_RDID        0x9f    /* Read Identification */
44 #define M25_RDSR        0x05    /* Read Status Register */
45 #define M25_WRSR        0x01    /* Write Status Register */
46 #define M25_READ        0x03    /* Read Data Bytes */
47 #define M25_FAST_READ   0x0b    /* Read Data Bytes at Higher Speed */
48 #define M25_PP          0x02    /* Page Program */
49 #define M25_SE          0xd8    /* Sector Erase */
50 #define M25_BE          0xc7    /* Bulk Erase */
51 #define M25_DP          0xb9    /* Deep Power-down */
52
53 /* RDID response */
54 #define M25_MANUF_OFFSET        0
55 #define M25_MEMORY_TYPE_OFFSET  1
56 #define M25_CAPACITY_OFFSET     2
57 #define M25_UID_OFFSET          3
58 #define M25_CFI_OFFSET          4
59 #define M25_RDID_LEN            4       /* that's all we need */
60
61 #define M25_CAPACITY_128KB      0x11
62 #define M25_CAPACITY_256KB      0x12
63 #define M25_CAPACITY_512KB      0x13
64 #define M25_CAPACITY_1MB        0x14
65 #define M25_CAPACITY_2MB        0x15
66
67 /*
68  * Status register bits
69  */
70
71 #define M25_STATUS_SRWD         (1 << 7)        /* Status register write disable */
72 #define M25_STATUS_BP_MASK      (7 << 2)        /* Block protect bits */
73 #define M25_STATUS_BP_SHIFT     (2)
74 #define M25_STATUS_WEL          (1 << 1)        /* Write enable latch */
75 #define M25_STATUS_WIP          (1 << 0)        /* Write in progress */
76
77 /*
78  * On teleterra, the m25 chip select pins are
79  * wired on P0_0 through P0_3.
80  */
81
82 #if M25_MAX_CHIPS > 1
83 static uint8_t ao_m25_size[M25_MAX_CHIPS];      /* number of sectors in each chip */
84 static uint8_t ao_m25_pin[M25_MAX_CHIPS];       /* chip select pin for each chip */
85 static uint8_t ao_m25_numchips;                 /* number of chips detected */
86 #endif
87 static uint8_t ao_m25_total;                    /* total sectors available */
88 static uint8_t ao_m25_wip;                      /* write in progress */
89
90 static __xdata uint8_t ao_m25_mutex;
91
92 /*
93  * This little array is abused to send and receive data. A particular
94  * caution -- the read and write addresses are written into the last
95  * three bytes of the array by ao_m25_set_page_address and then the
96  * first byte is used by ao_m25_wait_wip and ao_m25_write_enable, neither
97  * of which touch those last three bytes.
98  */
99
100 static __xdata uint8_t  ao_m25_instruction[4];
101
102 #if HAS_BOOT_RADIO
103 /* Kick any radio listeners off so the flash can be written */
104 extern uint8_t ao_radio_in_recv;
105
106 static void ao_boot_radio(void) {
107         if (ao_radio_in_recv)
108                 ao_radio_recv_abort();
109 }
110 #else
111 #define ao_boot_radio()
112 #endif
113
114 #define M25_SELECT(cs)          do { ao_boot_radio(); ao_spi_get_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS, AO_SPI_SPEED_FAST); } while (0)
115 #define M25_DESELECT(cs)        ao_spi_put_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS)
116
117 #define M25_BLOCK_SHIFT                 16
118 #define M25_BLOCK                       65536L
119 #define M25_POS_TO_SECTOR(pos)          ((uint8_t) ((pos) >> M25_BLOCK_SHIFT))
120 #define M25_SECTOR_TO_POS(sector)       (((uint32_t) (sector)) << M25_BLOCK_SHIFT)
121
122 /*
123  * Block until the specified chip is done writing
124  */
125 static void
126 ao_m25_wait_wip(uint8_t cs)
127 {
128         if (ao_m25_wip & cs) {
129                 M25_SELECT(cs);
130                 ao_m25_instruction[0] = M25_RDSR;
131                 ao_spi_send(ao_m25_instruction, 1, AO_M25_SPI_BUS);
132                 do {
133                         ao_spi_recv(ao_m25_instruction, 1, AO_M25_SPI_BUS);
134                 } while (ao_m25_instruction[0] & M25_STATUS_WIP);
135                 M25_DESELECT(cs);
136                 ao_m25_wip &= ~cs;
137         }
138 }
139
140 /*
141  * Set the write enable latch so that page program and sector
142  * erase commands will work. Also mark the chip as busy writing
143  * so that future operations will block until the WIP bit goes off
144  */
145 static void
146 ao_m25_write_enable(uint8_t cs)
147 {
148         M25_SELECT(cs);
149         ao_m25_instruction[0] = M25_WREN;
150         ao_spi_send(&ao_m25_instruction, 1, AO_M25_SPI_BUS);
151         M25_DESELECT(cs);
152         ao_m25_wip |= cs;
153 }
154
155
156 /*
157  * Returns the number of 64kB sectors
158  */
159 static uint8_t
160 ao_m25_read_capacity(uint8_t cs)
161 {
162         uint8_t capacity;
163         M25_SELECT(cs);
164         ao_m25_instruction[0] = M25_RDID;
165         ao_spi_send(ao_m25_instruction, 1, AO_M25_SPI_BUS);
166         ao_spi_recv(ao_m25_instruction, M25_RDID_LEN, AO_M25_SPI_BUS);
167         M25_DESELECT(cs);
168
169         /* Check to see if the chip is present */
170         if (ao_m25_instruction[0] == 0xff)
171                 return 0;
172         capacity = ao_m25_instruction[M25_CAPACITY_OFFSET];
173
174         /* Sanity check capacity number */
175         if (capacity < 0x11 || 0x1f < capacity)
176                 return 0;
177         return 1 << (capacity - 0x10);
178 }
179
180 static uint8_t
181 ao_m25_set_address(uint32_t pos)
182 {
183         uint8_t chip;
184 #if M25_MAX_CHIPS > 1
185         uint8_t size;
186
187         for (chip = 0; chip < ao_m25_numchips; chip++) {
188                 size = ao_m25_size[chip];
189                 if (M25_POS_TO_SECTOR(pos) < size)
190                         break;
191                 pos -= M25_SECTOR_TO_POS(size);
192         }
193         if (chip == ao_m25_numchips)
194                 return 0xff;
195
196         chip = ao_m25_pin[chip];
197 #else
198         chip = AO_M25_SPI_CS_MASK;
199 #endif
200         ao_m25_wait_wip(chip);
201
202         ao_m25_instruction[1] = pos >> 16;
203         ao_m25_instruction[2] = pos >> 8;
204         ao_m25_instruction[3] = pos;
205         return chip;
206 }
207
208 /*
209  * Scan the possible chip select lines
210  * to see which flash chips are connected
211  */
212 static uint8_t
213 ao_m25_scan(void)
214 {
215 #if M25_MAX_CHIPS > 1
216         uint8_t pin, size;
217 #endif
218
219         if (ao_m25_total)
220                 return 1;
221
222 #if M25_MAX_CHIPS > 1
223         ao_m25_numchips = 0;
224         for (pin = 1; pin != 0; pin <<= 1) {
225                 if (AO_M25_SPI_CS_MASK & pin) {
226                         size = ao_m25_read_capacity(pin);
227                         if (size != 0) {
228                                 ao_m25_size[ao_m25_numchips] = size;
229                                 ao_m25_pin[ao_m25_numchips] = pin;
230                                 ao_m25_total += size;
231                                 ao_m25_numchips++;
232                         }
233                 }
234         }
235 #else
236         ao_m25_total = ao_m25_read_capacity(AO_M25_SPI_CS_MASK);
237 #endif
238         if (!ao_m25_total)
239                 return 0;
240         ao_storage_total = M25_SECTOR_TO_POS(ao_m25_total);
241         ao_storage_block = M25_BLOCK;
242         ao_storage_config = ao_storage_total - M25_BLOCK;
243         ao_storage_unit = 256;
244         return 1;
245 }
246
247 /*
248  * Erase the specified sector
249  */
250 uint8_t
251 ao_storage_erase(uint32_t pos) __reentrant
252 {
253         uint8_t cs;
254
255         if (pos >= ao_storage_total || pos + ao_storage_block > ao_storage_total)
256                 return 0;
257
258         ao_mutex_get(&ao_m25_mutex);
259         ao_m25_scan();
260
261         cs = ao_m25_set_address(pos);
262
263         ao_m25_wait_wip(cs);
264         ao_m25_write_enable(cs);
265
266         ao_m25_instruction[0] = M25_SE;
267         M25_SELECT(cs);
268         ao_spi_send(ao_m25_instruction, 4, AO_M25_SPI_BUS);
269         M25_DESELECT(cs);
270         ao_m25_wip |= cs;
271
272         ao_mutex_put(&ao_m25_mutex);
273         return 1;
274 }
275
276 /*
277  * Write to flash
278  */
279 uint8_t
280 ao_storage_device_write(uint32_t pos, __xdata void *d, uint16_t len) __reentrant
281 {
282         uint8_t cs;
283
284         if (pos >= ao_storage_total || pos + len > ao_storage_total)
285                 return 0;
286
287         ao_mutex_get(&ao_m25_mutex);
288         ao_m25_scan();
289
290         cs = ao_m25_set_address(pos);
291         ao_m25_write_enable(cs);
292
293         ao_m25_instruction[0] = M25_PP;
294         M25_SELECT(cs);
295         ao_spi_send(ao_m25_instruction, 4, AO_M25_SPI_BUS);
296         ao_spi_send(d, len, AO_M25_SPI_BUS);
297         M25_DESELECT(cs);
298
299         ao_mutex_put(&ao_m25_mutex);
300         return 1;
301 }
302
303 /*
304  * Read from flash
305  */
306 uint8_t
307 ao_storage_device_read(uint32_t pos, __xdata void *d, uint16_t len) __reentrant
308 {
309         uint8_t cs;
310
311         if (pos >= ao_storage_total || pos + len > ao_storage_total)
312                 return 0;
313         ao_mutex_get(&ao_m25_mutex);
314         ao_m25_scan();
315
316         cs = ao_m25_set_address(pos);
317
318         /* No need to use the FAST_READ as we're running at only 8MHz */
319         ao_m25_instruction[0] = M25_READ;
320         M25_SELECT(cs);
321         ao_spi_send(ao_m25_instruction, 4, AO_M25_SPI_BUS);
322         ao_spi_recv(d, len, AO_M25_SPI_BUS);
323         M25_DESELECT(cs);
324
325         ao_mutex_put(&ao_m25_mutex);
326         return 1;
327 }
328
329 void
330 ao_storage_flush(void) __reentrant
331 {
332 }
333
334 void
335 ao_storage_setup(void)
336 {
337         ao_mutex_get(&ao_m25_mutex);
338         ao_m25_scan();
339         ao_mutex_put(&ao_m25_mutex);
340 }
341
342 void
343 ao_storage_device_info(void) __reentrant
344 {
345         uint8_t cs;
346 #if M25_MAX_CHIPS > 1
347         uint8_t chip;
348 #endif
349
350         ao_mutex_get(&ao_m25_mutex);
351         ao_m25_scan();
352         ao_mutex_put(&ao_m25_mutex);
353
354 #if M25_MAX_CHIPS > 1
355         printf ("Detected chips %d size %d\n", ao_m25_numchips, ao_m25_total);
356         for (chip = 0; chip < ao_m25_numchips; chip++)
357                 printf ("Flash chip %d select %02x size %d\n",
358                         chip, ao_m25_pin[chip], ao_m25_size[chip]);
359 #else
360         printf ("Detected chips 1 size %d\n", ao_m25_total);
361 #endif
362
363         printf ("Available chips:\n");
364         for (cs = 1; cs != 0; cs <<= 1) {
365                 if ((AO_M25_SPI_CS_MASK & cs) == 0)
366                         continue;
367
368                 ao_mutex_get(&ao_m25_mutex);
369                 M25_SELECT(cs);
370                 ao_m25_instruction[0] = M25_RDID;
371                 ao_spi_send(ao_m25_instruction, 1, AO_M25_SPI_BUS);
372                 ao_spi_recv(ao_m25_instruction, M25_RDID_LEN, AO_M25_SPI_BUS);
373                 M25_DESELECT(cs);
374
375                 printf ("Select %02x manf %02x type %02x cap %02x uid %02x\n",
376                         cs,
377                         ao_m25_instruction[M25_MANUF_OFFSET],
378                         ao_m25_instruction[M25_MEMORY_TYPE_OFFSET],
379                         ao_m25_instruction[M25_CAPACITY_OFFSET],
380                         ao_m25_instruction[M25_UID_OFFSET]);
381                 ao_mutex_put(&ao_m25_mutex);
382         }
383 }
384
385 void
386 ao_storage_device_init(void)
387 {
388         ao_spi_init_cs (AO_M25_SPI_CS_PORT, AO_M25_SPI_CS_MASK);
389 }