Mercurial > ~darius > hgwebdir.cgi > stm32temp
comparison spiflash.c @ 80:1a4573062b37
Reshuffle in preparation for being able to have a common API for SPI flash and (emulated) EEPROM.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Sun, 07 Jul 2013 22:49:02 +0930 |
parents | flash.c@85f16813c730 |
children | 05ba84c7da97 |
comparison
equal
deleted
inserted
replaced
79:cecb0506f4b8 | 80:1a4573062b37 |
---|---|
1 #include <stdio.h> | |
2 #include <stdint.h> | |
3 #include <string.h> | |
4 #include <stdlib.h> | |
5 #include <assert.h> | |
6 | |
7 #include "stm32f10x.h" | |
8 #include "spi.h" | |
9 #include "spiflash.h" | |
10 | |
11 #define FL_SELECT() GPIO_ResetBits(GPIOA, GPIO_Pin_4) | |
12 #define FL_DESELECT() GPIO_SetBits(GPIOA, GPIO_Pin_4) | |
13 | |
14 const char *flstattbl[] = { | |
15 "BUSY", | |
16 "WEL", | |
17 "BP0", | |
18 "BP1", | |
19 "BP2", | |
20 "BP3", | |
21 "AAI", | |
22 "BPL" | |
23 }; | |
24 | |
25 #define RW_IDLE 0 | |
26 #define RW_RUNNING 1 | |
27 | |
28 static int writestate = RW_IDLE; | |
29 static int readstate = RW_IDLE; | |
30 #if 0 | |
31 void | |
32 spiflashcmd(int argc, char **argv) { | |
33 uint8_t status, tmp, len; | |
34 uint32_t addr; | |
35 | |
36 if (argc == 0) { | |
37 fputs("No command specified\n", stdout); | |
38 return; | |
39 } | |
40 | |
41 if (!strcmp(argv[0], "str")) { | |
42 status = spiflashreadstatus(); | |
43 fputs("Status = ", stdout); | |
44 for (unsigned int i = 0; i < sizeof(flstattbl) / sizeof(flstattbl[0]); i++) | |
45 if (status & 1 << i) { | |
46 fputs(flstattbl[i], stdout); | |
47 fputs(" ", stdout); | |
48 } | |
49 printf("(0x%02x)\n", status); | |
50 } else if (!strcmp(argv[0], "stw")) { | |
51 if (argc != 2) { | |
52 fputs("Incorrect number of arguments\n", stdout); | |
53 return; | |
54 } | |
55 tmp = atoi(argv[1]); | |
56 spiflashwritestatus(tmp); | |
57 status = spiflashreadstatus(); | |
58 printf("Wrote 0x%02x to status, now 0x%02x\n", tmp, status); | |
59 } else if (!strcmp(argv[0], "er")) { | |
60 if (argc != 2) { | |
61 fputs("Incorrect number of arguments\n", stdout); | |
62 return; | |
63 } | |
64 addr = atoi(argv[1]); | |
65 spiflash4kerase(addr); | |
66 printf("Erased 0x%x\n", (unsigned int)addr); | |
67 } else if (!strcmp(argv[0], "rd")) { | |
68 if (argc < 2) { | |
69 fputs("Incorrect number of arguments\n", stdout); | |
70 return; | |
71 } | |
72 | |
73 addr = atoi(argv[1]); | |
74 | |
75 if (argc > 2) | |
76 len = atoi(argv[2]); | |
77 else | |
78 len = 16; | |
79 | |
80 spiflashstartread(addr); | |
81 | |
82 for (uint32_t i = 0; i < len; i++) | |
83 printf("Read 0x%02x from 0x%06x\n", spiflashreadbyte(), (unsigned int)(addr + i)); | |
84 spiflashstopread(); | |
85 | |
86 fputs("\n", stdout); | |
87 } else if (!strcmp(argv[0], "wr")) { | |
88 if (argc < 2) { | |
89 fputs("Incorrect number of arguments\n", stdout); | |
90 return; | |
91 } | |
92 | |
93 addr = atoi(argv[1]); | |
94 | |
95 if (argc > 2) | |
96 len = atoi(argv[2]); | |
97 else | |
98 len = 16; | |
99 | |
100 for (int i = 0; i < 16; i += 2) { | |
101 uint16_t data; | |
102 data = ((i + 1) << 8) | i; | |
103 printf("Writing 0x%04x to 0x%06x\n", data, (unsigned int)(addr + i)); | |
104 | |
105 if (i == 0) | |
106 spiflashstartwrite(addr, data); | |
107 else | |
108 spiflashwriteword(data); | |
109 } | |
110 spiflashstopwrite(); | |
111 } else if (!strcmp(argv[0], "id")) { | |
112 printf("Flash ID = 0x%04hx (expect 0xbf41)\n", spiflashreadid()); | |
113 } else { | |
114 fputs("Unknown sub command\n", stdout); | |
115 return; | |
116 } | |
117 } | |
118 #endif | |
119 void | |
120 spiflash4kerase(uint32_t addr) { | |
121 spiflashenablewrite(); /* Enable writing */ | |
122 | |
123 FL_SELECT(); /* Select device */ | |
124 | |
125 SPI_WriteByte(FL_4KERASE); /* Send command */ | |
126 SPI_WriteByte(addr >> 16); /* Send address */ | |
127 SPI_WriteByte(addr >> 8); | |
128 SPI_WriteByte(addr); | |
129 | |
130 FL_DESELECT(); | |
131 | |
132 //fputs("4k erase ", stdout); | |
133 spiflashwait(); | |
134 } | |
135 | |
136 void | |
137 spiflashwait(void) { | |
138 uint8_t cnt; | |
139 | |
140 /* Wait for not BUSY */ | |
141 for (cnt = 0; (spiflashreadstatus() & FL_BUSY) != 0; cnt++) | |
142 ; | |
143 | |
144 //printf("cnt = %d\n", cnt); | |
145 } | |
146 | |
147 uint16_t | |
148 spiflashreadid(void) { | |
149 uint8_t fac, dev; | |
150 | |
151 FL_SELECT(); /* Select device */ | |
152 | |
153 SPI_WriteByte(FL_RDID); /* Send command */ | |
154 SPI_WriteByte(0x00); /* Send address cycles (ID data starts at 0) */ | |
155 SPI_WriteByte(0x00); | |
156 SPI_WriteByte(0x00); | |
157 fac = SPI_WriteByte(0x00); /* Read ID */ | |
158 dev = SPI_WriteByte(0x00); | |
159 | |
160 FL_DESELECT(); /* De-select device */ | |
161 | |
162 return fac << 8 | dev; | |
163 } | |
164 | |
165 void | |
166 spiflashenablewrite(void) { | |
167 FL_SELECT(); /* Select device */ | |
168 | |
169 SPI_WriteByte(FL_WREN); /* Send command */ | |
170 | |
171 FL_DESELECT(); /* De-select device */ | |
172 } | |
173 | |
174 uint8_t | |
175 spiflashreadstatus(void) { | |
176 uint8_t status; | |
177 | |
178 FL_SELECT(); /* Select device */ | |
179 | |
180 SPI_WriteByte(FL_RDSR); /* Send command */ | |
181 SPI_WriteByte(0x00); /* Send dummy byte for address cycle */ | |
182 status = SPI_WriteByte(0x00); /* Read status */ | |
183 | |
184 FL_DESELECT(); /* De-select device */ | |
185 | |
186 return status; | |
187 } | |
188 | |
189 void | |
190 spiflashwritestatus(uint8_t status) { | |
191 /* Enable status write */ | |
192 FL_SELECT(); /* Select device */ | |
193 SPI_WriteByte(FL_EWSR); /* Send command */ | |
194 SPI_WriteByte(0x00); /* Send data byte */ | |
195 FL_DESELECT(); | |
196 | |
197 /* Actually write status */ | |
198 FL_SELECT(); /* Re-select device for new command */ | |
199 SPI_WriteByte(FL_WRSR); /* Send command */ | |
200 SPI_WriteByte(status); /* Send data byte */ | |
201 FL_DESELECT(); /* De-select device */ | |
202 } | |
203 | |
204 uint8_t | |
205 spiflashread(uint32_t addr) { | |
206 uint8_t data; | |
207 | |
208 FL_SELECT(); /* Select device */ | |
209 | |
210 SPI_WriteByte(FL_READ); /* Send command */ | |
211 SPI_WriteByte(addr >> 16); /* Send address */ | |
212 SPI_WriteByte(addr >> 8); | |
213 SPI_WriteByte(addr); | |
214 data = SPI_WriteByte(0x00); /* Read data */ | |
215 | |
216 FL_DESELECT(); /* De-select device */ | |
217 | |
218 return data; | |
219 } | |
220 | |
221 void | |
222 spiflashwrite(uint32_t addr, uint8_t data) { | |
223 spiflashwait(); | |
224 spiflashenablewrite(); /* Enable writes */ | |
225 | |
226 FL_SELECT(); /* Select device */ | |
227 | |
228 SPI_WriteByte(FL_BYTEPROG); /* Send command */ | |
229 SPI_WriteByte(addr >> 16); /* Send address */ | |
230 SPI_WriteByte(addr >> 8); | |
231 SPI_WriteByte(addr); | |
232 SPI_WriteByte(data); /* Write data */ | |
233 | |
234 FL_DESELECT(); /* De-select device */ | |
235 | |
236 } | |
237 | |
238 /* | |
239 * fStream reading looks like so | |
240 * | |
241 */ | |
242 | |
243 void | |
244 spiflashstartread(uint32_t addr) { | |
245 assert(readstate == RW_IDLE); | |
246 | |
247 FL_SELECT(); /* Select device */ | |
248 | |
249 SPI_WriteByte(FL_READ); /* Send command */ | |
250 SPI_WriteByte(addr >> 16); /* Send address */ | |
251 SPI_WriteByte(addr >> 8); | |
252 SPI_WriteByte(addr); | |
253 | |
254 readstate = RW_RUNNING; | |
255 } | |
256 | |
257 uint8_t | |
258 spiflashreadbyte(void) { | |
259 assert(readstate == RW_RUNNING); | |
260 return SPI_WriteByte(0x00); /* Read data */ | |
261 } | |
262 | |
263 void | |
264 spiflashstopread(void) { | |
265 assert(readstate == RW_RUNNING); | |
266 | |
267 FL_DESELECT(); | |
268 | |
269 readstate = RW_IDLE; | |
270 } | |
271 | |
272 /* | |
273 * Auto increment writing looks like so | |
274 * | |
275 * Enable writing CS, WREN, nCS | |
276 * Send start address & first data word CS, AAI + addr + data, nCS | |
277 * Send subsequent words wait for nBUSY, CS, AAI + data, nCS | |
278 * ... | |
279 * Disable writing CS, WRDI, nCS | |
280 * | |
281 * XXX: EBSY command links SO to flash busy state, I don't think the | |
282 * STM32 could sample it without switching out of SPI mode. | |
283 */ | |
284 void | |
285 spiflashstartwrite(uint32_t addr, uint16_t data) { | |
286 assert(writestate == RW_IDLE); | |
287 | |
288 spiflashenablewrite(); /* Enable writes */ | |
289 | |
290 FL_SELECT(); /* Select device */ | |
291 | |
292 SPI_WriteByte(FL_AAIWP); /* Send command */ | |
293 SPI_WriteByte(addr >> 16); | |
294 SPI_WriteByte(addr >> 8); | |
295 SPI_WriteByte(addr & 0xff); /* Send address */ | |
296 | |
297 SPI_WriteByte(data & 0xff); /* Write LSB */ | |
298 SPI_WriteByte(data >> 8); /* Write MSB */ | |
299 | |
300 FL_DESELECT(); | |
301 | |
302 writestate = RW_RUNNING; | |
303 } | |
304 | |
305 void | |
306 spiflashwriteword(uint16_t data) { | |
307 assert(writestate == RW_RUNNING); | |
308 | |
309 //fputs("write word ", stdout); | |
310 spiflashwait(); /* Wait until not busy */ | |
311 | |
312 FL_SELECT(); /* Select device */ | |
313 | |
314 SPI_WriteByte(FL_AAIWP); /* Send command */ | |
315 SPI_WriteByte(data & 0xff); /* Write LSB */ | |
316 SPI_WriteByte(data >> 8); /* Write MSB */ | |
317 | |
318 FL_DESELECT(); /* De-select device */ | |
319 } | |
320 | |
321 void | |
322 spiflashstopwrite(void) { | |
323 assert(writestate == RW_RUNNING); | |
324 | |
325 //fputs("flash stop write start ", stdout); | |
326 spiflashwait(); /* Wait until not busy */ | |
327 | |
328 FL_SELECT(); /* Select device */ | |
329 | |
330 SPI_WriteByte(FL_WRDI); /* Send command */ | |
331 | |
332 FL_DESELECT(); /* Deselect device */ | |
333 | |
334 //fputs("flash stop write end ", stdout); | |
335 spiflashwait(); /* Wait until not busy */ | |
336 | |
337 writestate = RW_IDLE; | |
338 } | |
339 | |
340 int | |
341 spiflashreadblock(uint32_t addr, uint32_t len, void *_data) { | |
342 uint8_t *data = _data; | |
343 uint32_t flashcrc, ramcrc; | |
344 | |
345 /* Must be a multiple of 4 due to CRC check */ | |
346 assert(len % 4 == 0); | |
347 | |
348 spiflashstartread(addr); | |
349 CRC_ResetDR(); | |
350 for (int i = len; i > 0; i--) { | |
351 *data = spiflashreadbyte(); | |
352 CRC_CalcCRC(*data); | |
353 data++; | |
354 } | |
355 | |
356 flashcrc = spiflashreadbyte(); | |
357 flashcrc |= spiflashreadbyte() << 8; | |
358 flashcrc |= spiflashreadbyte() << 16; | |
359 flashcrc |= spiflashreadbyte() << 24; | |
360 | |
361 spiflashstopread(); | |
362 | |
363 ramcrc = CRC_GetCRC(); | |
364 | |
365 /* printf("RAM CRC 0x%08x Flash CRC 0x%08x\n", (uint)ramcrc, (uint)flashcrc); */ | |
366 if (ramcrc == flashcrc) | |
367 return 1; | |
368 else | |
369 return 0; | |
370 } | |
371 | |
372 uint32_t | |
373 spiflashcrcblock(uint32_t addr, uint32_t len) { | |
374 assert(len % 4 == 0); | |
375 | |
376 CRC_ResetDR(); | |
377 | |
378 spiflashstartread(addr); | |
379 for (int i = len; i > 0; i--) | |
380 CRC_CalcCRC(spiflashreadbyte()); | |
381 | |
382 spiflashstopread(); | |
383 | |
384 return CRC_GetCRC(); | |
385 } | |
386 | |
387 int | |
388 spiflashwriteblock(uint32_t addr, uint32_t len, void *_data) { | |
389 uint16_t *data = _data; | |
390 uint32_t crc, vcrc; | |
391 | |
392 //printf("Writing %u bytes to 0x%06x\n", (uint)len, (uint)addr); | |
393 | |
394 /* Ensure data is | |
395 * - 16 bit aligned | |
396 * - a multiple of 32 bits in length (for CRCs, the flash only need 16 bits) | |
397 * - not longer than a sector | |
398 */ | |
399 assert(addr % 2 == 0); | |
400 assert(len % 4 == 0); | |
401 assert(len <= 4096); | |
402 | |
403 /* Disable write protect */ | |
404 spiflashwritestatus(0x00); | |
405 | |
406 /* Erase sector */ | |
407 spiflash4kerase(addr); | |
408 | |
409 CRC_ResetDR(); | |
410 | |
411 /* Write data */ | |
412 for (uint i = 0; i < len / 2; i++) { | |
413 if (i == 0) | |
414 spiflashstartwrite(addr, *data); | |
415 else | |
416 spiflashwriteword(*data); | |
417 CRC_CalcCRC(*data); | |
418 data++; | |
419 } | |
420 | |
421 /* Calculate CRC */ | |
422 crc = CRC_GetCRC(); | |
423 | |
424 //printf("CRC is 0x%08x\n", (uint)crc); | |
425 | |
426 /* Write CRC */ | |
427 spiflashwriteword(crc); | |
428 spiflashwriteword(crc >> 16); | |
429 | |
430 spiflashstopwrite(); | |
431 | |
432 /* Read back and check CRC */ | |
433 vcrc = spiflashcrcblock(addr, len); | |
434 if (vcrc != crc) | |
435 return 1; | |
436 else | |
437 return 0; | |
438 } | |
439 | |
440 void | |
441 spiflashprintstatus(uint8_t status, FILE *out) { | |
442 for (unsigned int i = 0; i < sizeof(flstattbl) / sizeof(flstattbl[0]); i++) | |
443 if (status & 1 << i) { | |
444 fputs(flstattbl[i], out); | |
445 fputs(" ", out); | |
446 } | |
447 } |