]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/omap2420h4/flash.c
* Patches by Richard Woodruff, 01 Oct 2004:
[karo-tx-uboot.git] / board / omap2420h4 / flash.c
1 /*
2  * (C) Copyright 2001
3  * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
4  *
5  * (C) Copyright 2001-2004
6  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
7  *
8  * (C) Copyright 2003
9  * Texas Instruments, <www.ti.com>
10  * Kshitij Gupta <Kshitij@ti.com>
11  *
12  * See file CREDITS for list of people who contributed to this
13  * project.
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License as
17  * published by the Free Software Foundation; either version 2 of
18  * the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
28  * MA 02111-1307 USA
29  */
30
31 #include <common.h>
32 #include <asm/arch/sizes.h>
33 #include <linux/byteorder/swab.h>
34
35 #define PHYS_FLASH_SECT_SIZE    SZ_128K
36 flash_info_t flash_info[CFG_MAX_FLASH_BANKS];   /* info for FLASH chips    */
37
38 /* Board support for 1 or 2 flash devices */
39 #undef FLASH_PORT_WIDTH32
40 #define FLASH_PORT_WIDTH16
41
42 #ifdef FLASH_PORT_WIDTH16
43 # define FLASH_PORT_WIDTH               ushort
44 # define FLASH_PORT_WIDTHV              vu_short
45 # define SWAP(x)                        __swab16(x)
46 #else
47 # define FLASH_PORT_WIDTH               ulong
48 # define FLASH_PORT_WIDTHV              vu_long
49 # define SWAP(x)                        __swab32(x)
50 #endif
51
52 #define FPW     FLASH_PORT_WIDTH
53 #define FPWV    FLASH_PORT_WIDTHV
54
55 #define mb() __asm__ __volatile__ ("" : : : "memory")
56
57
58 /* Flash Organization Structure */
59 typedef struct OrgDef {
60         unsigned int sector_number;
61         unsigned int sector_size;
62 } OrgDef;
63
64
65 /* Flash Organizations */
66 OrgDef OrgIntel_28F256L18T[] = {
67         {4, SZ_32K},            /* 4 * 32kBytes sectors */
68         {255, SZ_128K},         /* 255 * 128kBytes sectors */
69 };
70
71
72 /*-----------------------------------------------------------------------
73  * Functions
74  */
75 unsigned long flash_init (void);
76 static ulong flash_get_size (FPW * addr, flash_info_t * info);
77 static int write_data (flash_info_t * info, ulong dest, FPW data);
78 static void flash_get_offsets (ulong base, flash_info_t * info);
79 void inline spin_wheel (void);
80 void flash_print_info (flash_info_t * info);
81 void flash_unprotect_sectors (FPWV * addr);
82 int flash_erase (flash_info_t * info, int s_first, int s_last);
83 int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt);
84 void flash_unlock(flash_info_t * info, int bank);
85 int flash_probe(void);
86
87 /*-----------------------------------------------------------------------
88  */
89
90 /* see if flash is ok */
91 int flash_probe(void)
92 {
93         return(flash_get_size ((FPW *) PHYS_FLASH_1, &flash_info[0]));
94 }
95
96 unsigned long flash_init (void)
97 {
98         int i;
99         ulong size = 0;
100         for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
101                 switch (i) {
102                 case 0:
103                         flash_get_size ((FPW *) PHYS_FLASH_1, &flash_info[i]);
104                         flash_get_offsets (PHYS_FLASH_1, &flash_info[i]);
105                         /* to reset the lock bit */
106                         flash_unlock(&flash_info[i],i);
107                         break;
108                 case 1:
109                         flash_get_size ((FPW *) PHYS_FLASH_2, &flash_info[i]);
110                         flash_get_offsets (PHYS_FLASH_2, &flash_info[i]);
111                         /* to reset the lock bit */
112                         flash_unlock(&flash_info[i],i);
113                         break;
114
115                 default:
116                         panic ("configured too many flash banks!\n");
117                         break;
118                 }
119                 size += flash_info[i].size;
120         }
121
122         /* Protect monitor and environment sectors
123          */
124         flash_protect (FLAG_PROTECT_SET,
125                                    CFG_FLASH_BASE,
126                                    CFG_FLASH_BASE + monitor_flash_len - 1, &flash_info[0]);
127
128         flash_protect (FLAG_PROTECT_SET,
129                                    CFG_ENV_ADDR,
130                                    CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]);
131
132         return size;
133 }
134
135 /*-----------------------------------------------------------------------
136  */
137 void flash_unlock(flash_info_t * info, int bank)
138 {
139         int j;
140         if (!bank)
141                 j=2;    /* leave 0,1 locked for boot bank */
142         else
143                 j=0;    /* get the whole bank for #2 */
144
145         for (;j<CFG_MAX_FLASH_SECT;j++) {
146                 FPWV *addr = (FPWV *) (info->start[j]);
147                 if (addr == NULL) {
148                         printf("Warning Flash probe failed\n");
149                         break;
150                 }
151                 flash_unprotect_sectors (addr);
152                 *addr = (FPW) 0x00500050;/* clear status register */
153                 *addr = (FPW) 0x00FF00FF;/* resest to read mode */
154         }
155 }
156
157 /*-----------------------------------------------------------------------
158  */
159 static void flash_get_offsets (ulong base, flash_info_t * info)
160 {
161         int i;
162         volatile int r;  /* gcc 3.4.0-1 strangeness, need to follow up.*/
163
164         if (info->flash_id == FLASH_UNKNOWN) {
165                 return;
166         }
167
168         if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) {
169                 for (i = 0; i < info->sector_count; i++) {
170                         if (i > 254) { /* 255,256,257,258 */
171                                 r=i;
172                                 info->start[i] = base + (((r-(int)255) * SZ_32K) + (255*PHYS_FLASH_SECT_SIZE));
173                                 info->protect[i] = 0;
174                         } else {
175                                 info->start[i] = base + (i * PHYS_FLASH_SECT_SIZE);
176                                 info->protect[i] = 0;
177                         }
178                 }
179         }
180 }
181
182 /*-----------------------------------------------------------------------
183  */
184 void flash_print_info (flash_info_t * info)
185 {
186         int i;
187
188         if (info->flash_id == FLASH_UNKNOWN) {
189                 printf ("missing or unknown FLASH type\n");
190                 return;
191         }
192
193         switch (info->flash_id & FLASH_VENDMASK) {
194         case FLASH_MAN_INTEL:
195                 printf ("INTEL ");
196                 break;
197         default:
198                 printf ("Unknown Vendor ");
199                 break;
200         }
201
202         switch (info->flash_id & FLASH_TYPEMASK) {
203         case FLASH_28F256L18T:
204                 printf ("FLASH 28F256L18T\n");
205                 break;
206         default:
207                 printf ("Unknown Chip Type\n");
208                 break;
209         }
210
211         printf ("  Size: %ld MB in %d Sectors\n",
212                         info->size >> 20, info->sector_count);
213
214         printf ("  Sector Start Addresses:");
215         for (i = 0; i < info->sector_count; ++i) {
216                 if ((i % 5) == 0)
217                         printf ("\n   ");
218                 printf (" %08lX%s",
219                                 info->start[i], info->protect[i] ? " (RO)" : "     ");
220         }
221         printf ("\n");
222         return;
223 }
224
225 /*
226  * The following code cannot be run from FLASH!
227  */
228 static ulong flash_get_size (FPW * addr, flash_info_t * info)
229 {
230         volatile FPW value;
231         /* mb();  this one makes ARM11 err go away, but I want it :) as a guide to problems */
232
233         /* Write auto select command: read Manufacturer ID */
234         addr[0x5555] = (FPW) 0x00AA00AA;
235         addr[0x2AAA] = (FPW) 0x00550055;
236         addr[0x5555] = (FPW) 0x00900090;
237
238         mb ();
239         value = addr[0] & 0xFF; /* just looking for 89 (8989 is hw pat)*/
240
241         switch (value) {
242
243         case (FPW) INTEL_MANUFACT:
244                 info->flash_id = FLASH_MAN_INTEL;
245                 break;
246
247         default:
248                 info->flash_id = FLASH_UNKNOWN;
249                 info->sector_count = 0;
250                 info->size = 0;
251                 addr[0] = (FPW) 0x00FF00FF;     /* restore read mode */
252                 return(0);                 /* no or unknown flash       */
253         }
254
255         mb ();
256         value = addr[1];        /* device ID */
257         switch (value) {
258
259         case (FPW) (INTEL_ID_28F256L18T):        /* 880D */
260                 info->flash_id += FLASH_28F256L18T;
261                 info->sector_count = 259;       /*0-258*/
262                 info->size = SZ_32M;
263                 break;                  /* => 32 MB     */
264
265         default:
266                 info->flash_id = FLASH_UNKNOWN;
267                 break;
268         }
269
270         if (info->sector_count > CFG_MAX_FLASH_SECT) {
271                 printf ("** ERROR: sector count %d > max (%d) **\n",
272                                 info->sector_count, CFG_MAX_FLASH_SECT);
273                 info->sector_count = CFG_MAX_FLASH_SECT;
274         }
275
276         addr[0] = (FPW) 0x00FF00FF;     /* restore read mode */
277
278         return(info->size);
279 }
280
281
282 /* unprotects a sector for write and erase
283  * on some intel parts, this unprotects the entire chip, but it
284  * wont hurt to call this additional times per sector...
285  */
286 void flash_unprotect_sectors (FPWV * addr)
287 {
288 #define PD_FINTEL_WSMS_READY_MASK    0x0080
289
290         *addr = (FPW) 0x00500050;       /* clear status register */
291
292         /* this sends the clear lock bit command */
293         *addr = (FPW) 0x00600060;
294         *addr = (FPW) 0x00D000D0;
295 }
296
297
298 /*-----------------------------------------------------------------------
299  */
300
301 int flash_erase (flash_info_t * info, int s_first, int s_last)
302 {
303         int prot, sect;
304         ulong type, start, last;
305         int rcode = 0;
306 #ifdef CONFIG_USE_IRQ
307         int iflag;
308 #endif
309
310         if ((s_first < 0) || (s_first > s_last)) {
311                 if (info->flash_id == FLASH_UNKNOWN) {
312                         printf ("- missing\n");
313                 } else {
314                         printf ("- no sectors to erase\n");
315                 }
316                 return 1;
317         }
318
319         type = (info->flash_id & FLASH_VENDMASK);
320         if ((type != FLASH_MAN_INTEL)) {
321                 printf ("Can't erase unknown flash type %08lx - aborted\n",
322                                 info->flash_id);
323                 return 1;
324         }
325
326         prot = 0;
327         for (sect = s_first; sect <= s_last; ++sect) {
328                 if (info->protect[sect]) {
329                         prot++;
330                 }
331         }
332
333         if (prot) {
334                 printf ("- Warning: %d protected sectors will not be erased!\n",
335                                 prot);
336         } else {
337                 printf ("\n");
338         }
339
340
341         start = get_timer (0);
342         last = start;
343
344 #ifdef CONFIG_USE_IRQ
345         /* Disable interrupts which might cause a timeout here */
346         iflag = disable_interrupts ();
347 #endif
348
349         /* Start erase on unprotected sectors */
350         for (sect = s_first; sect <= s_last; sect++) {
351                 if (info->protect[sect] == 0) { /* not protected */
352                         FPWV *addr = (FPWV *) (info->start[sect]);
353                         FPW status;
354
355                         printf ("Erasing sector %2d ... ", sect);
356
357                         flash_unprotect_sectors (addr);
358
359                         /* arm simple, non interrupt dependent timer */
360                         reset_timer_masked ();
361
362                         *addr = (FPW) 0x00500050;/* clear status register */
363                         *addr = (FPW) 0x00200020;/* erase setup */
364                         *addr = (FPW) 0x00D000D0;/* erase confirm */
365
366                         while (((status =
367                                          *addr) & (FPW) 0x00800080) !=
368                                    (FPW) 0x00800080) {
369                                 if (get_timer_masked () >
370                                         CFG_FLASH_ERASE_TOUT) {
371                                         printf ("Timeout\n");
372                                         /* suspend erase     */
373                                         *addr = (FPW) 0x00B000B0;
374                                         /* reset to read mode */
375                                         *addr = (FPW) 0x00FF00FF;
376                                         rcode = 1;
377                                         break;
378                                 }
379                         }
380
381                         /* clear status register cmd.   */
382                         *addr = (FPW) 0x00500050;
383                         *addr = (FPW) 0x00FF00FF;/* resest to read mode */
384                         printf (" done\n");
385                 }
386         }
387 #ifdef CONFIG_USE_IRQ
388         if (iflag)
389                 enable_interrupts();
390 #endif
391
392         return rcode;
393 }
394
395 /*-----------------------------------------------------------------------
396  * Copy memory to flash, returns:
397  * 0 - OK
398  * 1 - write timeout
399  * 2 - Flash not erased
400  * 4 - Flash not identified
401  */
402
403 int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
404 {
405         ulong cp, wp;
406         FPW data;
407         int count, i, l, rc, port_width;
408
409         if (info->flash_id == FLASH_UNKNOWN) {
410                 return 4;
411         }
412 /* get lower word aligned address */
413 #ifdef FLASH_PORT_WIDTH16
414         wp = (addr & ~1);
415         port_width = 2;
416 #else
417         wp = (addr & ~3);
418         port_width = 4;
419 #endif
420
421         /*
422          * handle unaligned start bytes
423          */
424         if ((l = addr - wp) != 0) {
425                 data = 0;
426                 for (i = 0, cp = wp; i < l; ++i, ++cp) {
427                         data = (data << 8) | (*(uchar *) cp);
428                 }
429                 for (; i < port_width && cnt > 0; ++i) {
430                         data = (data << 8) | *src++;
431                         --cnt;
432                         ++cp;
433                 }
434                 for (; cnt == 0 && i < port_width; ++i, ++cp) {
435                         data = (data << 8) | (*(uchar *) cp);
436                 }
437
438                 if ((rc = write_data (info, wp, SWAP (data))) != 0) {
439                         return(rc);
440                 }
441                 wp += port_width;
442         }
443
444         /*
445          * handle word aligned part
446          */
447         count = 0;
448         while (cnt >= port_width) {
449                 data = 0;
450                 for (i = 0; i < port_width; ++i) {
451                         data = (data << 8) | *src++;
452                 }
453                 if ((rc = write_data (info, wp, SWAP (data))) != 0) {
454                         return(rc);
455                 }
456                 wp += port_width;
457                 cnt -= port_width;
458                 if (count++ > 0x800) {
459                         spin_wheel ();
460                         count = 0;
461                 }
462         }
463
464         if (cnt == 0) {
465                 return(0);
466         }
467
468         /*
469          * handle unaligned tail bytes
470          */
471         data = 0;
472         for (i = 0, cp = wp; i < port_width && cnt > 0; ++i, ++cp) {
473                 data = (data << 8) | *src++;
474                 --cnt;
475         }
476         for (; i < port_width; ++i, ++cp) {
477                 data = (data << 8) | (*(uchar *) cp);
478         }
479
480         return(write_data (info, wp, SWAP (data)));
481 }
482
483 /*-----------------------------------------------------------------------
484  * Write a word or halfword to Flash, returns:
485  * 0 - OK
486  * 1 - write timeout
487  * 2 - Flash not erased
488  */
489 static int write_data (flash_info_t * info, ulong dest, FPW data)
490 {
491         FPWV *addr = (FPWV *) dest;
492         ulong status;
493 #ifdef CONFIG_USE_IRQ
494         int iflag;
495 #endif
496
497         /* Check if Flash is (sufficiently) erased */
498         if ((*addr & data) != data) {
499                 printf ("not erased at %08lx (%x)\n", (ulong) addr, *addr);
500                 return(2);
501         }
502         /* Disable interrupts which might cause a timeout here */
503 #ifdef CONFIG_USE_IRQ
504         iflag = disable_interrupts ();
505 #endif
506         *addr = (FPW) 0x00400040;       /* write setup */
507         *addr = data;
508
509         /* arm simple, non interrupt dependent timer */
510         reset_timer_masked ();
511
512         /* wait while polling the status register */
513         while (((status = *addr) & (FPW) 0x00800080) != (FPW) 0x00800080) {
514                 if (get_timer_masked () > CFG_FLASH_WRITE_TOUT) {
515                         *addr = (FPW) 0x00FF00FF;       /* restore read mode */
516                         return(1);
517                 }
518         }
519         *addr = (FPW) 0x00FF00FF;       /* restore read mode */
520
521 #ifdef CONFIG_USE_IRQ
522         if (iflag)
523                 enable_interrupts();
524 #endif
525
526         return(0);
527 }
528
529 void inline spin_wheel (void)
530 {
531         static int p = 0;
532         static char w[] = "\\/-";
533
534         printf ("\010%c", w[p]);
535         (++p == 3) ? (p = 0) : 0;
536 }