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