1 | /* $Id$ */ |
---|
2 | |
---|
3 | /* |
---|
4 | * Trivial driver for spansion flash present on the |
---|
5 | * MVME3100 board. |
---|
6 | * |
---|
7 | * For recognized devices, look for 'spansionDevs'. |
---|
8 | * |
---|
9 | * This driver has only been tested with stride=4 |
---|
10 | * and in 16-bit mode (width=2). |
---|
11 | */ |
---|
12 | |
---|
13 | /* |
---|
14 | * Authorship |
---|
15 | * ---------- |
---|
16 | * This software was created by |
---|
17 | * Till Straumann <strauman@slac.stanford.edu>, 2005-2007, |
---|
18 | * Stanford Linear Accelerator Center, Stanford University. |
---|
19 | * |
---|
20 | * Acknowledgement of sponsorship |
---|
21 | * ------------------------------ |
---|
22 | * The software was produced by |
---|
23 | * the Stanford Linear Accelerator Center, Stanford University, |
---|
24 | * under Contract DE-AC03-76SFO0515 with the Department of Energy. |
---|
25 | * |
---|
26 | * Government disclaimer of liability |
---|
27 | * ---------------------------------- |
---|
28 | * Neither the United States nor the United States Department of Energy, |
---|
29 | * nor any of their employees, makes any warranty, express or implied, or |
---|
30 | * assumes any legal liability or responsibility for the accuracy, |
---|
31 | * completeness, or usefulness of any data, apparatus, product, or process |
---|
32 | * disclosed, or represents that its use would not infringe privately owned |
---|
33 | * rights. |
---|
34 | * |
---|
35 | * Stanford disclaimer of liability |
---|
36 | * -------------------------------- |
---|
37 | * Stanford University makes no representations or warranties, express or |
---|
38 | * implied, nor assumes any liability for the use of this software. |
---|
39 | * |
---|
40 | * Stanford disclaimer of copyright |
---|
41 | * -------------------------------- |
---|
42 | * Stanford University, owner of the copyright, hereby disclaims its |
---|
43 | * copyright and all other rights in this software. Hence, anyone may |
---|
44 | * freely use it for any purpose without restriction. |
---|
45 | * |
---|
46 | * Maintenance of notices |
---|
47 | * ---------------------- |
---|
48 | * In the interest of clarity regarding the origin and status of this |
---|
49 | * SLAC software, this and all the preceding Stanford University notices |
---|
50 | * are to remain affixed to any copy or derivative of this software made |
---|
51 | * or distributed by the recipient and are to be affixed to any copy of |
---|
52 | * software made or distributed by the recipient that contains a copy or |
---|
53 | * derivative of this software. |
---|
54 | * |
---|
55 | * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 |
---|
56 | */ |
---|
57 | |
---|
58 | #include <rtems.h> |
---|
59 | #include <stdio.h> |
---|
60 | #include <inttypes.h> |
---|
61 | |
---|
62 | #include <bsp/flashPgmPvt.h> |
---|
63 | |
---|
64 | #define DEBUG 5 |
---|
65 | #undef DEBUG |
---|
66 | |
---|
67 | #ifdef DEBUG |
---|
68 | #define STATIC |
---|
69 | #else |
---|
70 | #define STATIC static |
---|
71 | #endif |
---|
72 | |
---|
73 | /* Manual says max erase time is 3.5 s */ |
---|
74 | #define ERASE_TIMEOUT 4 /* seconds */ |
---|
75 | #define WRITE_TIMEOUT 1000 /* us; manual says: 240us typ. */ |
---|
76 | |
---|
77 | /* Assume flash-endianness == CPU endianness */ |
---|
78 | |
---|
79 | #ifdef __PPC__ |
---|
80 | #define IOSYNC(mem) do { asm volatile("eieio"); } while (0) |
---|
81 | #else |
---|
82 | #define IOSYNC(mem) do { } while (0) |
---|
83 | #endif |
---|
84 | |
---|
85 | /********* Forward Declarations ****************/ |
---|
86 | |
---|
87 | STATIC int |
---|
88 | flash_get_id_s160(struct bankdesc *, uint32_t , uint32_t *, uint32_t *); |
---|
89 | |
---|
90 | STATIC void |
---|
91 | flash_unlock_block_s160(struct bankdesc *, uint32_t); |
---|
92 | |
---|
93 | STATIC void |
---|
94 | flash_lock_block_s160(struct bankdesc *, uint32_t); |
---|
95 | |
---|
96 | STATIC int |
---|
97 | flash_erase_block_s160(struct bankdesc *, uint32_t); |
---|
98 | |
---|
99 | STATIC uint32_t |
---|
100 | flash_check_ready_s160(struct bankdesc *, uint32_t); |
---|
101 | |
---|
102 | STATIC void |
---|
103 | flash_print_stat_s160(struct bankdesc *, uint32_t, int); |
---|
104 | |
---|
105 | STATIC void |
---|
106 | flash_array_mode_s160(struct bankdesc *, uint32_t); |
---|
107 | |
---|
108 | STATIC uint32_t |
---|
109 | flash_write_line_s160(struct bankdesc *, uint32_t, char *, uint32_t); |
---|
110 | |
---|
111 | /********* Global Variables ********************/ |
---|
112 | |
---|
113 | static struct flash_bank_ops spansionOps = { |
---|
114 | get_id : flash_get_id_s160, |
---|
115 | unlock_block: flash_unlock_block_s160, |
---|
116 | lock_block : flash_lock_block_s160, |
---|
117 | erase_block : flash_erase_block_s160, |
---|
118 | check_ready : flash_check_ready_s160, |
---|
119 | print_stat : flash_print_stat_s160, |
---|
120 | array_mode : flash_array_mode_s160, |
---|
121 | write_line : flash_write_line_s160, |
---|
122 | }; |
---|
123 | |
---|
124 | static struct devdesc spansionDevs[] = { |
---|
125 | { 0x007e2101, "S29GL128N", 0x01000000, 32, 0x20000 }, /* 16MB */ |
---|
126 | { 0x007e2201, "S29GL256N", 0x02000000, 32, 0x20000 }, /* 32MB */ |
---|
127 | { 0x007e2301, "S29GL512N", 0x04000000, 32, 0x20000 }, /* 64MB */ |
---|
128 | { 0, 0, 0, 0} |
---|
129 | }; |
---|
130 | |
---|
131 | struct vendesc BSP_flash_vendor_spansion[] = { |
---|
132 | { 0x01, "Spansion/AMD", spansionDevs, &spansionOps }, |
---|
133 | { 0, 0} |
---|
134 | }; |
---|
135 | |
---|
136 | /********* Register Definitions ****************/ |
---|
137 | |
---|
138 | #define UNLK1_ADDR_16 0x555 |
---|
139 | #define UNLK1_DATA 0xaa |
---|
140 | #define UNLK2_ADDR_16 0x2aa |
---|
141 | #define UNLK2_ADDR_8 0x555 |
---|
142 | #define UNLK2_DATA 0x55 |
---|
143 | #define ASEL_DATA 0x90 |
---|
144 | #define VEND_ID_ADDR_16 0x000 |
---|
145 | #define SPROT_ADDR_16 0x002 |
---|
146 | #define DEV1_ID_ADDR_16 0x001 |
---|
147 | #define DEV2_ID_ADDR_16 0x00e |
---|
148 | #define DEV3_ID_ADDR_16 0x00f |
---|
149 | #define ERASE_DATA 0x80 |
---|
150 | #define SECT_ERASE_DATA 0x30 |
---|
151 | #define DQ7_DATA 0x80 |
---|
152 | #define RESET_DATA 0xf0 |
---|
153 | #define WRBUF_DATA 0x25 |
---|
154 | #define PGBUF_DATA 0x29 |
---|
155 | |
---|
156 | #define DQ7_POLL_ALL (-1) |
---|
157 | |
---|
158 | /********* Helper Types ************************/ |
---|
159 | |
---|
160 | union bconv { |
---|
161 | uint32_t u; |
---|
162 | uint16_t s[2]; |
---|
163 | char c[4]; |
---|
164 | }; |
---|
165 | |
---|
166 | /********* Register Access Primitives **********/ |
---|
167 | |
---|
168 | /* All of these currently assume stride == 4, i.e. |
---|
169 | * two 16-bit devices or 4 8-bit devices in parallel. |
---|
170 | * |
---|
171 | * FIXME: |
---|
172 | * 8-bit mode and strides 1,2 untested. |
---|
173 | */ |
---|
174 | |
---|
175 | #define ADDR32(b, a, o) ((a) + ((o)*FLASH_STRIDE(b))) |
---|
176 | |
---|
177 | static inline uint32_t |
---|
178 | fl_rd32(struct bankdesc *b, uint32_t a, uint32_t off) |
---|
179 | { |
---|
180 | volatile union bconv *p; |
---|
181 | uint32_t rval; |
---|
182 | |
---|
183 | if ( 1 == b->width ) |
---|
184 | off <<= 1;; |
---|
185 | |
---|
186 | a = ADDR32(b, a, off); |
---|
187 | |
---|
188 | p = (volatile union bconv *)a; |
---|
189 | if ( 4 == FLASH_STRIDE(b) ) { |
---|
190 | rval = p->u; |
---|
191 | IOSYNC(p->u); |
---|
192 | } else if ( 2 == FLASH_STRIDE(b) ) { |
---|
193 | rval = p->s[0]; |
---|
194 | IOSYNC(p->s[0]); |
---|
195 | } else { |
---|
196 | rval = p->c[0]; |
---|
197 | IOSYNC(p->c[0]); |
---|
198 | } |
---|
199 | return rval; |
---|
200 | } |
---|
201 | |
---|
202 | static inline void |
---|
203 | fl_wr32(struct bankdesc *b, uint32_t a, uint32_t v) |
---|
204 | { |
---|
205 | volatile union bconv *p = (volatile union bconv*)a; |
---|
206 | if ( 4 == FLASH_STRIDE(b) ) { |
---|
207 | p->u = v; |
---|
208 | IOSYNC(p->u); |
---|
209 | } else if ( 2 == FLASH_STRIDE(b) ) { |
---|
210 | p->s[0] = v; |
---|
211 | IOSYNC(p->s[0]); |
---|
212 | } else { |
---|
213 | p->c[0] = v; |
---|
214 | IOSYNC(p->c[0]); |
---|
215 | } |
---|
216 | } |
---|
217 | |
---|
218 | static inline uint32_t |
---|
219 | fl_splat32(struct bankdesc *b, uint32_t x) |
---|
220 | { |
---|
221 | if ( 4 == FLASH_STRIDE(b) ) { |
---|
222 | if ( 1 == b->width ) { |
---|
223 | x = (x << 8) | x; |
---|
224 | } |
---|
225 | x = (x<<16) | x; |
---|
226 | } else if ( 2 == FLASH_STRIDE(b) ) { |
---|
227 | if ( 1 == b->width ) |
---|
228 | x = (x << 8) | x; |
---|
229 | } |
---|
230 | return x; |
---|
231 | } |
---|
232 | |
---|
233 | static inline uint32_t |
---|
234 | fl_x32(struct bankdesc *b, union bconv *pv) |
---|
235 | { |
---|
236 | if ( 4 == FLASH_STRIDE(b) ) |
---|
237 | return pv->u; |
---|
238 | else if ( 2 == FLASH_STRIDE(b) ) |
---|
239 | return pv->s[0]; |
---|
240 | else |
---|
241 | return pv->c[0]; |
---|
242 | } |
---|
243 | |
---|
244 | static inline void |
---|
245 | fl_wr32_cmd(struct bankdesc *b, uint32_t a, uint32_t off, uint32_t cmd) |
---|
246 | { |
---|
247 | if ( 1 == b->width ) { |
---|
248 | if ( off == UNLK2_ADDR_16 ) |
---|
249 | off = UNLK2_ADDR_8; |
---|
250 | else |
---|
251 | /* all others are simply left shifted */ |
---|
252 | off <<= 1; |
---|
253 | } |
---|
254 | cmd = fl_splat32(b, cmd); |
---|
255 | a = ADDR32(b, a, off); |
---|
256 | fl_wr32(b, a, cmd); |
---|
257 | } |
---|
258 | |
---|
259 | /* Send unlock sequence */ |
---|
260 | static inline void unlk(struct bankdesc *b, uint32_t a) |
---|
261 | { |
---|
262 | a &= ~ ( ADDR32(b, 0,0x1000) - 1 ); |
---|
263 | fl_wr32_cmd(b, a, UNLK1_ADDR_16, UNLK1_DATA); |
---|
264 | fl_wr32_cmd(b, a, UNLK2_ADDR_16, UNLK2_DATA); |
---|
265 | } |
---|
266 | |
---|
267 | /********* Helper Routines *********************/ |
---|
268 | |
---|
269 | STATIC int |
---|
270 | sector_is_protected(struct bankdesc *b, uint32_t addr) |
---|
271 | { |
---|
272 | int rval; |
---|
273 | unlk(b, addr); |
---|
274 | fl_wr32_cmd(b, addr, UNLK1_ADDR_16, ASEL_DATA); |
---|
275 | rval = fl_rd32(b, addr, SPROT_ADDR_16); |
---|
276 | flash_array_mode_s160(b, addr); |
---|
277 | return rval; |
---|
278 | } |
---|
279 | |
---|
280 | STATIC int fl_dq7_poll(struct bankdesc *b, uint32_t addr, uint32_t d7_val) |
---|
281 | { |
---|
282 | d7_val &= fl_splat32(b, DQ7_DATA); |
---|
283 | return ( (fl_rd32(b, addr, 0) & fl_splat32(b, DQ7_DATA)) == d7_val ); |
---|
284 | } |
---|
285 | |
---|
286 | /* Do DQ7 polling until DQ7 reads the value passed in d7_val |
---|
287 | * or timeout |
---|
288 | */ |
---|
289 | STATIC int |
---|
290 | flash_pend(struct bankdesc *b, uint32_t addr, uint32_t timeout_us, uint32_t d7_val) |
---|
291 | { |
---|
292 | uint32_t then = BSP_flashBspOps.read_us_timer(); |
---|
293 | uint32_t now = then; |
---|
294 | |
---|
295 | do { |
---|
296 | if ( fl_dq7_poll(b, addr, d7_val) ) { |
---|
297 | #if (DEBUG > 4) |
---|
298 | printf("Write buffer succeded after %"PRIi32"us\n", (now-then)*8/333); |
---|
299 | #endif |
---|
300 | return 0; |
---|
301 | } |
---|
302 | now = BSP_flashBspOps.read_us_timer(); |
---|
303 | } while ( now - then < timeout_us ); |
---|
304 | |
---|
305 | return -1; |
---|
306 | } |
---|
307 | |
---|
308 | |
---|
309 | /********* Access Methods **********************/ |
---|
310 | |
---|
311 | STATIC void |
---|
312 | flash_array_mode_s160(struct bankdesc *b, uint32_t addr) |
---|
313 | { |
---|
314 | fl_wr32_cmd(b, addr, 0, RESET_DATA); |
---|
315 | } |
---|
316 | |
---|
317 | STATIC int |
---|
318 | flash_get_id_s160(struct bankdesc *b, uint32_t addr, uint32_t *pVendorId, uint32_t *pDeviceId) |
---|
319 | { |
---|
320 | uint32_t dev_id[3], x, i; |
---|
321 | |
---|
322 | if ( 4 != FLASH_STRIDE(b) ) |
---|
323 | fprintf(stderr,"Warning: strides other than 4 untested\n(%s at %d)\n", |
---|
324 | __FILE__,__LINE__); |
---|
325 | |
---|
326 | if ( 2 != b->width ) |
---|
327 | fprintf(stderr,"Warning: device width other than 2 untested\n(%s at %d)\n", |
---|
328 | __FILE__,__LINE__); |
---|
329 | |
---|
330 | addr &= ~ (ADDR32(b, 0, 0x1000) - 1); |
---|
331 | unlk(b, addr); |
---|
332 | fl_wr32_cmd(b, addr, UNLK1_ADDR_16, ASEL_DATA); |
---|
333 | *pVendorId = fl_rd32(b, addr, VEND_ID_ADDR_16) & 0xff; |
---|
334 | dev_id [0] = fl_rd32(b, addr, DEV1_ID_ADDR_16); |
---|
335 | dev_id [1] = fl_rd32(b, addr, DEV2_ID_ADDR_16); |
---|
336 | dev_id [2] = fl_rd32(b, addr, DEV3_ID_ADDR_16); |
---|
337 | |
---|
338 | #ifdef DEBUG |
---|
339 | printf("Vendor Id 0x%08"PRIx32", Dev Ids: 0x%08"PRIx32", 0x%08"PRIx32", 0x%08"PRIx32"\n", |
---|
340 | *pVendorId, dev_id[0], dev_id[1], dev_id[2]); |
---|
341 | #endif |
---|
342 | |
---|
343 | flash_array_mode_s160(b, addr); |
---|
344 | |
---|
345 | for ( x=0, i=0; i<3; i++ ) { |
---|
346 | x = (x<<8) | (dev_id[i] & 0xff); |
---|
347 | } |
---|
348 | |
---|
349 | *pDeviceId = x; |
---|
350 | |
---|
351 | return 0; |
---|
352 | } |
---|
353 | |
---|
354 | |
---|
355 | STATIC void |
---|
356 | flash_lock_block_s160(struct bankdesc *b, uint32_t addr) |
---|
357 | { |
---|
358 | } |
---|
359 | |
---|
360 | STATIC void |
---|
361 | flash_unlock_block_s160(struct bankdesc *b, uint32_t addr) |
---|
362 | { |
---|
363 | } |
---|
364 | |
---|
365 | STATIC uint32_t |
---|
366 | flash_check_ready_s160(struct bankdesc *b, uint32_t addr) |
---|
367 | { |
---|
368 | flash_array_mode_s160(b, addr); |
---|
369 | return 0; |
---|
370 | } |
---|
371 | |
---|
372 | /* Erase single block holding 'addr'ess |
---|
373 | * |
---|
374 | * RETURNS: zero on error, device status on failure. |
---|
375 | * |
---|
376 | * NOTES: - device switched back to array mode on exit. |
---|
377 | * - 'addr' must be 32-bit aligned. |
---|
378 | */ |
---|
379 | STATIC int |
---|
380 | flash_erase_block_s160(struct bankdesc *b, uint32_t addr) |
---|
381 | { |
---|
382 | rtems_interval p,i; |
---|
383 | |
---|
384 | addr &= ~ (b->fblksz-1); |
---|
385 | |
---|
386 | if ( sector_is_protected(b, addr) ) { |
---|
387 | fprintf(stderr,"Sector at 0x%08"PRIx32" is protected\n", addr); |
---|
388 | return -10; |
---|
389 | } |
---|
390 | |
---|
391 | unlk(b, addr); |
---|
392 | fl_wr32_cmd(b, addr, UNLK1_ADDR_16, ERASE_DATA); |
---|
393 | unlk(b, addr); |
---|
394 | fl_wr32_cmd(b, addr, 0, SECT_ERASE_DATA); |
---|
395 | |
---|
396 | p = rtems_clock_get_ticks_per_second(); |
---|
397 | p *= ERASE_TIMEOUT; |
---|
398 | |
---|
399 | for ( i=p; i; i-- ) { |
---|
400 | rtems_task_wake_after(1); |
---|
401 | if ( fl_dq7_poll(b, addr, DQ7_POLL_ALL) ) { |
---|
402 | break; |
---|
403 | } |
---|
404 | } |
---|
405 | #ifdef DEBUG |
---|
406 | printf("ERASE polled for %"PRIi32" ticks\n", p-i); |
---|
407 | #endif |
---|
408 | flash_array_mode_s160(b, addr); |
---|
409 | |
---|
410 | if ( i ) { |
---|
411 | /* write successful; verify */ |
---|
412 | for ( i = 0; i < b->fblksz; i++ ) { |
---|
413 | if ( 0xff != ((char*)addr)[i] ) { |
---|
414 | fprintf(stderr,"ERROR: Erase verification failed at %p\n", |
---|
415 | ((char*)addr) + i); |
---|
416 | return -1; |
---|
417 | } |
---|
418 | } |
---|
419 | return 0; |
---|
420 | } |
---|
421 | return -1; |
---|
422 | } |
---|
423 | |
---|
424 | STATIC void |
---|
425 | flash_print_stat_s160(struct bankdesc *b, uint32_t sta, int verbose) |
---|
426 | { |
---|
427 | fprintf(stderr,"Flash Spansion 160 error %"PRIi32"\n", sta); |
---|
428 | } |
---|
429 | |
---|
430 | STATIC uint32_t |
---|
431 | flash_write_line_s160(struct bankdesc *b, uint32_t a, char *s, uint32_t N) |
---|
432 | { |
---|
433 | uint32_t sta, nxt, j, v; |
---|
434 | union bconv buf; |
---|
435 | |
---|
436 | if ( 0 == N ) |
---|
437 | return -11; |
---|
438 | |
---|
439 | if ( N & (FLASH_STRIDE(b) - 1) ) { |
---|
440 | fprintf(stderr,"flash_write_line_s160: invalid byte count (not multiple of stride\n"); |
---|
441 | return -10; |
---|
442 | } |
---|
443 | |
---|
444 | unlk(b, a); |
---|
445 | |
---|
446 | /* address block */ |
---|
447 | fl_wr32_cmd(b, a, 0, WRBUF_DATA); |
---|
448 | |
---|
449 | /* (16-bit) word count per device */ |
---|
450 | N /= FLASH_STRIDE(b); |
---|
451 | |
---|
452 | fl_wr32_cmd(b, a, 0, N-1); |
---|
453 | |
---|
454 | /* silence compiler warning about uninitialized var (N > 0 at this point) */ |
---|
455 | v = 0; |
---|
456 | |
---|
457 | /* fill buffer */ |
---|
458 | for (nxt = a; N>0; N--) { |
---|
459 | #if (DEBUG > 4) |
---|
460 | printf("Writing DAT *0x%08"PRIx32" = 0x%08"PRIx32"\n", nxt, *(uint32_t*)s); |
---|
461 | #endif |
---|
462 | /* deal with misaligned sources */ |
---|
463 | for ( j=0; j<FLASH_STRIDE(b); j++ ) { |
---|
464 | buf.c[j] = *s++; |
---|
465 | } |
---|
466 | v = fl_x32(b, &buf); |
---|
467 | fl_wr32(b, nxt, v); |
---|
468 | nxt += FLASH_STRIDE(b); |
---|
469 | } |
---|
470 | |
---|
471 | /* burn buffer */ |
---|
472 | fl_wr32_cmd(b, a, 0, PGBUF_DATA); |
---|
473 | |
---|
474 | /* pend */ |
---|
475 | |
---|
476 | sta = flash_pend(b, nxt - FLASH_STRIDE(b), WRITE_TIMEOUT, v); |
---|
477 | |
---|
478 | return sta; |
---|
479 | } |
---|