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