]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/devs/flash/arm/mxc/v2_0/src/spi_nor.c
Initial revision
[karo-tx-redboot.git] / packages / devs / flash / arm / mxc / v2_0 / src / spi_nor.c
1 //==========================================================================
2 //
3 //      spi_nor.c
4 //
5 //      SPI NOR flash support
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //========================================================================*/
41
42 #include <pkgconf/hal.h>
43 #include <pkgconf/system.h>
44 #include <redboot.h>
45 #include CYGHWR_MEMORY_LAYOUT_H
46 #include <cyg/hal/hal_io.h>
47 #define  _FLASH_PRIVATE_
48 #include <cyg/io/flash.h>
49
50 #include <cyg/io/imx_spi.h>
51 #include <cyg/io/imx_spi_nor.h>
52
53 static unsigned char g_tx_buf[256];
54 static unsigned char g_rx_buf[256];
55 static int spi_nor_init_ok;
56
57 #define WRITE_ENABLE()          spi_nor_cmd_1byte(WREN)
58 #define WRITE_DISABLE()         spi_nor_cmd_1byte(WRDI)
59 #define ENABLE_WRITE_STATUS()   spi_nor_cmd_1byte(EWSR)
60
61 #ifndef MXCFLASH_SELECT_MULTI
62 void flash_query(void* data)
63 #else
64 void spi_norflash_query(void* data)
65 #endif
66 {
67     unsigned char tmp[4];
68     unsigned char *ptr = (unsigned char *)data;
69
70     g_tx_buf[3] = JEDEC_ID;
71     if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, tmp, 4) != 0) {
72         return;
73     }
74     diag_printf("JEDEC ID: 0x%02x:0x%02x:0x%02x\n", tmp[2], tmp[1], tmp[0]);
75     ptr[0] = tmp[2];
76     ptr[1] = tmp[1];
77     ptr[2] = tmp[0];
78 }
79
80 #ifndef MXCFLASH_SELECT_MULTI
81 int flash_program_buf(void* addr, void* data, int len)
82 #else
83 int spi_norflash_program_buf(void* addr, void* data, int len)
84 #endif
85 {
86     return spi_nor_program_buf(addr, data, len);
87 }
88
89 #ifndef MXCFLASH_SELECT_MULTI
90 int flash_erase_block(void* block, unsigned int size)
91 #else
92 int spi_norflash_erase_block(void* block, unsigned int size)
93 #endif
94 {
95     return spi_nor_erase_block(block, size);
96 }
97
98 #ifndef MXCFLASH_SELECT_MULTI
99 bool flash_code_overlaps(void *start, void *end)
100 #else
101 bool spi_norflash_code_overlaps(void *start, void *end)
102 #endif
103 {
104     extern unsigned char _stext[], _etext[];
105
106     return ((((unsigned long)&_stext >= (unsigned long)start) &&
107              ((unsigned long)&_stext < (unsigned long)end)) ||
108             (((unsigned long)&_etext >= (unsigned long)start) &&
109              ((unsigned long)&_etext < (unsigned long)end)));
110 }
111
112 #ifndef MXCFLASH_SELECT_MULTI
113 int flash_hwr_map_error(int e)
114 #else
115 int spi_norflash_hwr_map_error(int e)
116 #endif
117 {
118     return e;
119 }
120
121 //----------------------------------------------------------------------------
122 // Now that device properties are defined, include magic for defining
123 // accessor type and constants.
124 #include <cyg/io/flash_dev.h>
125
126 // Information about supported devices
127 typedef struct flash_dev_info {
128     cyg_uint8   device_id;
129     cyg_uint8   device_id2;
130     cyg_uint8   device_id3;
131     cyg_uint8   device_id4;
132     cyg_uint32  block_size;
133     cyg_int32   block_count;
134     cyg_uint32  device_size;
135     cyg_uint32  fis_start_addr;
136     cyg_uint8   vendor_info[96];
137 } __attribute__((aligned(4),packed))flash_dev_info_t;
138
139 static const flash_dev_info_t* flash_dev_info;
140 static const flash_dev_info_t supported_devices[] = {
141 #include <cyg/io/spi_nor_parts.inl>
142 };
143
144 #define NUM_DEVICES (sizeof(supported_devices)/sizeof(flash_dev_info_t))
145
146 #define ASSERT_SPI_NOR_INIT()       \
147     do {                                                                    \
148         if (spi_nor_init(&imx_spi_nor) != 0) {                              \
149             diag_printf("Error: failed to initialize SPI NOR\n");           \
150             return -1;                                                      \
151         }                                                                   \
152     } while (0);                                                            \
153
154 int
155 #ifndef MXCFLASH_SELECT_MULTI
156 flash_hwr_init(void)
157 #else
158 spi_norflash_hwr_init(void)
159 #endif
160 {
161     cyg_uint8 id[4];
162     int i;
163
164     if (!spi_nor_init_ok) {
165         diag_printf("Initializing SPI-NOR flash...\n");
166         if (spi_nor_init(&imx_spi_nor) != 0) {
167             diag_printf("Error: failed to initialize SPI NOR\n");
168             return -1;
169         }
170         spi_nor_init_ok = 1;
171     }
172     // Look through table for device data
173     flash_dev_query(id);
174     flash_dev_info = supported_devices;
175     for (i = 0; i < NUM_DEVICES; i++) {
176         if ((flash_dev_info->device_id == id[0]) &&
177             (flash_dev_info->device_id2 == id[1]) &&
178             (flash_dev_info->device_id3 == id[2]))
179             break;
180         flash_dev_info++;
181     }
182
183     // Do we find the device? If not, return error.
184     if (NUM_DEVICES == i) {
185         diag_printf("Unrecognized SPI NOR part: 0x%02x, 0x%02x, 0x%02x\n",
186                     id[0], id[1], id[2]);
187         return FLASH_ERR_DRV_WRONG_PART;
188     }
189
190     // Hard wired for now
191     flash_info.block_size = flash_dev_info->block_size;
192     flash_info.blocks = flash_dev_info->block_count;
193     flash_info.start = (void *)0;
194     flash_info.end = (void *)flash_dev_info->device_size;
195
196     diag_printf("SPI NOR: block_size=0x%x, blocks=0x%x, start=%p, end=%p\n",
197                flash_info.block_size, flash_info.blocks,
198                flash_info.start, flash_info.end);
199
200     return FLASH_ERR_OK;
201 }
202
203 // used by redboot/current/src/flash.c
204 int mxc_spi_nor_fis_start(void)
205 {
206     return (flash_dev_info->fis_start_addr);
207 }
208
209 static int spi_nor_cmd_1byte(unsigned char cmd)
210 {
211     g_tx_buf[0] = cmd;
212     if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, g_rx_buf, 1) != 0) {
213         diag_printf("Error: %s(): %d\n", __FUNCTION__, __LINE__);
214         return -1;
215     }
216     return 0;
217 }
218
219 /*!
220  * Read from SPI NOR at src address to RAM at dest with len bytes
221  * @param   src     source address in the flash
222  * @param   dest    destination address in the RAM
223  * @param   len     # of bytes to copy
224  */
225 int spi_nor_read(void *src, void *dest, int len)
226 {
227     unsigned int *cmd = (unsigned int *)g_tx_buf;
228     unsigned int max_rx_sz = imx_spi_nor.fifo_sz - 4; // max rx bytes per burst
229     unsigned char *d_buf = (unsigned char *) dest;
230     unsigned char *s_buf;
231     int i;
232
233     imx_spi_nor.us_delay = 100;
234     diag_printf1("%s(from flash=%p to ram=%p len=0x%x)\n", __FUNCTION__,
235                 src, dest, len);
236
237     if (len == 0)
238         return 0;
239
240     *cmd = (READ << 24) | ((unsigned int)src & 0x00FFFFFF);
241
242     while (1) {
243         if (len == 0) {
244             imx_spi_nor.us_delay = 0;
245             return 0;
246         }
247         if (len < max_rx_sz) {
248             diag_printf1("last read len=0x%x\n", len);
249             // deal with the last read
250             if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, g_rx_buf, len + 4) != 0) {
251                 diag_printf("Error: %s(%d): failed\n", __FILE__, __LINE__);
252                 return -1;
253             }
254             s_buf = g_rx_buf + 4;   // throw away 4 bytes (5th received bytes is real)
255             // now adjust the endianness
256             for (i = len; i >= 0; i -= 4, s_buf += 4) {
257                 if (i < 4) {
258                     if (i == 1) {
259                         *d_buf = s_buf[0];
260                     } else if (i == 2) {
261                         *d_buf++ = s_buf[1];
262                         *d_buf++ = s_buf[0];
263                     } else if (i == 3) {
264                         *d_buf++ = s_buf[2];
265                         *d_buf++ = s_buf[1];
266                         *d_buf++ = s_buf[0];
267                     }
268                     imx_spi_nor.us_delay = 0;
269                     return 0;
270                 }
271                 // copy 4 bytes
272                 *d_buf++ = s_buf[3];
273                 *d_buf++ = s_buf[2];
274                 *d_buf++ = s_buf[1];
275                 *d_buf++ = s_buf[0];
276             }
277         }
278         // now grab max_rx_sz data (+4 is needed due to 4-throw away bytes
279         if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, g_rx_buf, max_rx_sz + 4) != 0) {
280             diag_printf("Error: %s(%d): failed\n", __FILE__, __LINE__);
281             return -1;
282         }
283         s_buf = g_rx_buf + 4;   // throw away 4 bytes (5th received bytes is real)
284         // now adjust the endianness
285         for (i = 0; i < max_rx_sz; i += 4, s_buf += 4) {
286             *d_buf++ = s_buf[3];
287             *d_buf++ = s_buf[2];
288             *d_buf++ = s_buf[1];
289             *d_buf++ = s_buf[0];
290         }
291         *cmd += max_rx_sz;  // increase # of bytes in NOR address as cmd == g_tx_buf
292         len -= max_rx_sz;   // # of bytes left
293
294         diag_printf1("d_buf=%p, g_rx_buf=%p, len=0x%x\n", d_buf, g_rx_buf, len);
295     }
296
297     imx_spi_nor.us_delay = 0;
298 }
299
300 static int spi_nor_program_1byte(unsigned char data, void *addr)
301 {
302     unsigned int addr_val = (unsigned int) addr;
303
304     // need to do write-enable command
305     if (WRITE_ENABLE() != 0) {
306         diag_printf("Error : %d\n", __LINE__);
307         return -1;
308     }
309     g_tx_buf[0] = BYTE_PROG;    // need to skip bytes 1, 2, 3
310     g_tx_buf[4] = data;
311     g_tx_buf[5] = addr_val & 0xFF;
312     g_tx_buf[6] = (addr_val >> 8) & 0xFF;
313     g_tx_buf[7] = (addr_val >> 16) & 0xFF;
314
315     diag_printf("0x%x: 0x%x\n", *(unsigned int*)g_tx_buf, *(unsigned int*)(g_tx_buf + 4));
316     diag_printf("addr=0x%x\n", addr_val);
317
318     if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, g_rx_buf, 5) != 0) {
319         diag_printf("Error: %s(%d): failed\n", __FILE__, __LINE__);
320         return -1;
321     }
322
323     while (spi_nor_status() & RDSR_BUSY) {
324     }
325     return 0;
326 }
327
328 /*!
329  * program data from RAM to flash
330  * @param addr          destination address in flash
331  * @param data          source address in RAM
332  * @param len           # of bytes to program
333  * Note: - when starting AAI programming, 
334  *       1) the starting addr has to be 16-bit aligned
335  *       2) the prog len has to be even number of bytes
336  */
337 int spi_nor_program_buf(void *addr, void *data, int len)
338 {
339     unsigned int d_addr = (unsigned int) addr;
340     unsigned char *s_buf = (unsigned char *) data;
341
342     if (len == 0)
343         return 0;
344
345     diag_printf1("%s(flash addr=%p, ram=%p, len=0x%x)\n", __FUNCTION__, addr, data, len);
346     imx_spi_nor.us_delay = 0;
347
348     if (ENABLE_WRITE_STATUS() != 0 || spi_nor_write_status(0) != 0) {
349         diag_printf("Error: %s: %d\n", __FUNCTION__, __LINE__);
350         return -1;
351     }
352
353     if ((d_addr & 1) != 0) {
354         // program 1st byte
355         if (spi_nor_program_1byte(s_buf[0], (void *)d_addr) != 0) {
356             diag_printf("Error: %s(%d)\n", __FUNCTION__, __LINE__);
357             return -1;
358         }
359         if (--len == 0)
360             return 0;
361         d_addr++;
362         s_buf++;
363     }
364
365     // need to do write-enable command
366     if (WRITE_ENABLE() != 0) {
367         diag_printf("Error : %d\n", __LINE__);
368         return -1;
369     }
370
371     // These two bytes write will be copied to txfifo first with
372     // g_tx_buf[1] being shifted out and followed by g_tx_buf[0].
373     // The reason for this is we will specify burst len=6. So SPI will
374     // do this kind of data movement.
375     g_tx_buf[0] = d_addr >> 16;
376     g_tx_buf[1] = AAI_PROG;    // need to skip bytes 1, 2
377     // byte shifted order is: 7, 6, 5, 4
378     g_tx_buf[4] = s_buf[1];
379     g_tx_buf[5] = s_buf[0];
380     g_tx_buf[6] = d_addr;
381     g_tx_buf[7] = d_addr >> 8;    
382     if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, g_rx_buf, 6) != 0) {
383         diag_printf("Error: %s(%d): failed\n", __FILE__, __LINE__);
384         return -1;
385     }
386
387     while (spi_nor_status() & RDSR_BUSY) {
388     }
389
390     for (d_addr += 2, s_buf += 2, len -= 2 ;
391          len > 1;
392          d_addr += 2, s_buf += 2, len -= 2) {
393         // byte shifted order is: 2,1,0
394         g_tx_buf[2] = AAI_PROG;
395         g_tx_buf[1] = s_buf[0];
396         g_tx_buf[0] = s_buf[1];
397
398         if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, g_rx_buf, 3) != 0) {
399             diag_printf("Error: %s(%d): failed\n", __FILE__, __LINE__);
400             return -1;
401         }
402
403         while (spi_nor_status() & RDSR_BUSY) {
404         }
405         if ((len % flash_dev_info->block_size) == 0) {
406             diag_printf(".");
407         }
408     }
409     WRITE_DISABLE();
410     while (spi_nor_status() & RDSR_BUSY) {
411     }
412
413     if (WRITE_ENABLE() != 0) {
414         diag_printf("Error : %d\n", __LINE__);
415         return -1;
416     }
417     if (len == 1) {
418         // need to do write-enable command
419         // only 1 byte left
420         if (spi_nor_program_1byte(s_buf[0], (void *)d_addr) != 0) {
421             diag_printf("Error: %s(%d)\n", __FUNCTION__, __LINE__);
422             return -1;
423         }
424     }
425     return 0;
426 }
427
428 static int spi_nor_status(void)
429 {
430     g_tx_buf[1] = RDSR;
431     if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, g_rx_buf, 2) != 0) {
432         diag_printf("Error: %s(): %d\n", __FUNCTION__, __LINE__);
433         return 0;
434     }
435     return g_rx_buf[0];
436 }
437
438 /*!
439  * Write 'val' to flash WRSR (write status register)
440  */
441 static int spi_nor_write_status(unsigned char val)
442 {
443     g_tx_buf[0] = val;
444     g_tx_buf[1] = WRSR;
445     if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, g_rx_buf, 2) != 0) {
446         diag_printf("Error: %s(): %d\n", __FUNCTION__, __LINE__);
447         return -1;
448     }
449     return 0;
450 }
451
452 /*!
453  * Erase a block_size data from block_addr offset in the flash
454  */
455 int spi_nor_erase_block(void* block_addr, unsigned int block_size)
456 {
457     unsigned int *cmd = (unsigned int *)g_tx_buf;
458     unsigned int addr = (unsigned int) block_addr;
459
460     imx_spi_nor.us_delay = 0;
461     
462     if (block_size != SZ_64K && block_size != SZ_32K && block_size != SZ_4K) {
463         diag_printf("Error - block_size is not 64kB: 0x%x\n", block_size);
464         return -1;
465     }
466
467     if ((addr & (block_size -1)) != 0) {
468         diag_printf("Error - block_addr is not 64kB aligned: %p\n", block_addr);
469         return -1;
470     }
471     if (ENABLE_WRITE_STATUS() != 0 || spi_nor_write_status(0) != 0) {
472         diag_printf("Error: %s: %d\n", __FUNCTION__, __LINE__);
473         return -1;
474     }
475
476     // need to do write-enable command
477     if (WRITE_ENABLE() != 0) {
478         diag_printf("Error : %d\n", __LINE__);
479         return -1;
480     }
481
482     if (block_size == SZ_64K) {
483         *cmd = (ERASE_64K << 24) | (addr & 0x00FFFFFF);
484     } else if (block_size == SZ_32K) {
485         *cmd = (ERASE_32K << 24) | (addr & 0x00FFFFFF);
486     } else if (block_size == SZ_4K) {
487         *cmd = (ERASE_4K << 24) | (addr & 0x00FFFFFF);
488     }
489
490     // now do the block erase
491     if (spi_nor_xfer(&imx_spi_nor, g_tx_buf, g_rx_buf, 4) != 0) {
492         return -1;
493     }
494
495     while (spi_nor_status() & RDSR_BUSY) {
496     }
497     return 0;
498 }
499
500 /*!
501  * Erase a variable bytes data from SPI NOR flash for 64K blocks
502  * @param block_addr        starting addresss in the SPI NOR flash
503  * @param size              # of bytes to erase
504  */
505 int spi_nor_erase_64k(void* block_addr, unsigned int size)
506 {
507     unsigned int addr = (unsigned int) block_addr;
508
509     if ((size % SZ_64K) != 0 || size == 0) {
510         diag_printf("Error: size (0x%x) is not integer multiples of 64kB(0x10000)\n", size);
511         return -1;
512     }
513     if ((addr & (SZ_64K -1)) != 0) {
514         diag_printf("Error - addr is not 64kB(0x10000) aligned: %p\n", block_addr);
515         return -1;
516     }
517     for (; size > 0; size -= SZ_64K, addr += SZ_64K) {
518         if (spi_nor_erase_block((void *)addr, SZ_64K) != 0) {
519             diag_printf("Error: spi_nor_erase_64k(): %d\n", __LINE__);
520             return -1;
521         }
522     }
523     return 0;
524 }
525
526 void spi_nor_setup(void)
527 {
528     if (!spi_nor_init_ok) {
529         diag_printf("Initializing SPI-NOR flash...\n");
530         if (spi_nor_init(&imx_spi_nor) != 0) {
531             diag_printf("Error: failed to initialize SPI NOR\n");
532         }
533         spi_nor_init_ok = 1;
534     }
535 }
536
537 RedBoot_init(spi_nor_setup, RedBoot_INIT_PRIO(6800));
538
539 ////////////////////////////// commands ///////////////////
540 static void do_spi_nor_op(int argc, char *argv[]);
541 RedBoot_cmd("spiflash",
542             "Read/Write/Erase SPI NOR flash",
543             "<ram-addr> <flash-addr> <len-bytes> <r/w/e>",
544             do_spi_nor_op
545            );
546
547 static void do_spi_nor_op(int argc,char *argv[])
548 {
549     unsigned int ram, flash, len;
550     unsigned char op;
551     int stat = -1;
552
553     if (argc == 1 || argc != 5) {
554         diag_printf("\tRead:  spiflash <ram-addr> <flash-addr> <len-bytes> <r>\n");
555         diag_printf("\tWrite: spiflash <ram-addr> <flash-addr> <len-bytes> <w>\n");
556         diag_printf("\tErase: spiflash <ram-addr> <flash-addr> <len-bytes> <e>\n");
557         diag_printf("    NOTE: For erase, the ram-addr is ignored\n");
558         return;
559     }
560
561     if (!parse_num(*(&argv[1]), (unsigned long *)&ram, &argv[1], ":")) {
562         diag_printf("Error: Invalid ram parameter\n");
563         return;
564     }
565
566     if (!parse_num(*(&argv[2]), (unsigned long *)&flash, &argv[2], ":")) {
567         diag_printf("Error: Invalid flash parameter\n");
568         return;
569     }
570
571     if (!parse_num(*(&argv[3]), (unsigned long *)&len, &argv[3], ":")) {
572         diag_printf("Error: Invalid length parameter\n");
573         return;
574     }
575
576     op = argv[4][0];
577     switch (op) {
578     case 'r':
579     case 'R':
580         diag_printf("Reading SPI NOR flash 0x%x [0x%x bytes] -> ram 0x%x\n", flash, len, ram);
581         stat = spi_nor_read((void *)flash, (void *)ram, len);
582         break;
583     case 'w':
584     case 'W':
585         diag_printf("Writing SPI NOR flash 0x%x [0x%x bytes] <- ram 0x%x\n", flash, len, ram);
586         stat = spi_nor_program_buf((void *)flash, (void *)ram, len);
587         break;
588     case 'e':
589     case 'E':
590         diag_printf("Erasing SPI NOR flash 0x%x [0x%x bytes]\n", flash, len);
591         stat = spi_nor_erase_64k((void *)flash, len);
592         break;
593     default:
594         diag_printf("Error: unknown operation: 0x%02x\n", op);
595     }
596     diag_printf("%s\n\n", (stat == 0)? "SUCCESS": "FAILED");
597     return;
598 }