📄 cmd_nand.c.svn-base
字号:
/* * Driver for NAND support, Rick Bronson * borrowed heavily from: * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> */#include <common.h>#include <command.h>#include <malloc.h>#include <asm/io.h>#include <watchdog.h>#ifdef CONFIG_SHOW_BOOT_PROGRESS# include <status_led.h># define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg)#else# define SHOW_BOOT_PROGRESS(arg)#endif#if (CONFIG_COMMANDS & CFG_CMD_NAND)#include <linux/mtd/nand.h>#include <linux/mtd/nand_ids.h>#include <jffs2/jffs2.h>#ifdef CONFIG_OMAP1510void archflashwp(void *archdata, int wp);#endif#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1)))/* * Definition of the out of band configuration structure */struct nand_oob_config { int ecc_pos[6]; /* position of ECC bytes inside oob */ int badblock_pos; /* position of bad block flag inside oob -1 = inactive */ int eccvalid_pos; /* position of ECC valid flag inside oob -1 = inactive */} oob_config = { {0}, 0, 0};#undef NAND_DEBUG#undef PSYCHO_DEBUG/* ****************** WARNING ********************* * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will * erase (or at least attempt to erase) blocks that are marked * bad. This can be very handy if you are _sure_ that the block * is OK, say because you marked a good block bad to test bad * block handling and you are done testing, or if you have * accidentally marked blocks bad. * * Erasing factory marked bad blocks is a _bad_ idea. If the * erase succeeds there is no reliable way to find them again, * and attempting to program or erase bad blocks can affect * the data in _other_ (good) blocks. */#define ALLOW_ERASE_BAD_DEBUG 0#define CONFIG_MTD_NAND_ECC /* enable ECC */#define CONFIG_MTD_NAND_ECC_JFFS2/* bits for nand_rw() `cmd'; or together as needed */#define NANDRW_WRITE 0x00#define NANDRW_READ 0x01#define NANDRW_JFFS2 0x02#define NANDRW_JFFS2_SKIP 0x04/* * Function Prototypes */static void nand_print(struct nand_chip *nand);static int nand_rw (struct nand_chip* nand, int cmd, size_t start, size_t len,size_t * retlen, u_char * buf);static int nand_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean);static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, size_t * retlen, u_char *buf, u_char *ecc_code);static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, size_t * retlen, const u_char * buf, u_char * ecc_code);static void nand_print_bad(struct nand_chip *nand);static int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, size_t * retlen, u_char * buf);static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, size_t * retlen, const u_char * buf);static int NanD_WaitReady(struct nand_chip *nand, int ale_wait);#ifdef CONFIG_MTD_NAND_ECCstatic int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);#endifstruct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE] = {{0}};/* Current NAND Device */static int curr_device = -1;/* ------------------------------------------------------------------------- */int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){ int rcode = 0; switch (argc) { case 0: case 1: printf ("Usage:\n%s\n", cmdtp->usage); return 1; case 2: if (strcmp(argv[1],"info") == 0) { int i; putc ('\n'); for (i=0; i<CFG_MAX_NAND_DEVICE; ++i) { if(nand_dev_desc[i].ChipID == NAND_ChipID_UNKNOWN) continue; /* list only known devices */ printf ("Device %d: ", i); nand_print(&nand_dev_desc[i]); } return 0; } else if (strcmp(argv[1],"device") == 0) { if ((curr_device < 0) || (curr_device >= CFG_MAX_NAND_DEVICE)) { puts ("\nno devices available\n"); return 1; } printf ("\nDevice %d: ", curr_device); nand_print(&nand_dev_desc[curr_device]); return 0; } else if (strcmp(argv[1],"bad") == 0) { if ((curr_device < 0) || (curr_device >= CFG_MAX_NAND_DEVICE)) { puts ("\nno devices available\n"); return 1; } printf ("\nDevice %d bad blocks:\n", curr_device); nand_print_bad(&nand_dev_desc[curr_device]); return 0; } printf ("Usage:\n%s\n", cmdtp->usage); return 1; case 3: if (strcmp(argv[1],"device") == 0) { int dev = (int)simple_strtoul(argv[2], NULL, 10); printf ("\nDevice %d: ", dev); if (dev >= CFG_MAX_NAND_DEVICE) { puts ("unknown device\n"); return 1; } nand_print(&nand_dev_desc[dev]); /*nand_print (dev);*/ if (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN) { return 1; } curr_device = dev; puts ("... is now current device\n"); return 0; } else if (strcmp(argv[1],"erase") == 0 && strcmp(argv[2], "clean") == 0) { struct nand_chip* nand = &nand_dev_desc[curr_device]; ulong off = 0; ulong size = nand->totlen; int ret; printf ("\nNAND erase: device %d offset %ld, size %ld ... ", curr_device, off, size); ret = nand_erase (nand, off, size, 1); printf("%s\n", ret ? "ERROR" : "OK"); return ret; } printf ("Usage:\n%s\n", cmdtp->usage); return 1; default: /* at least 4 args */ if (strncmp(argv[1], "read", 4) == 0 || strncmp(argv[1], "write", 5) == 0) { ulong addr = simple_strtoul(argv[2], NULL, 16); ulong off = simple_strtoul(argv[3], NULL, 16); ulong size = simple_strtoul(argv[4], NULL, 16); int cmd = (strncmp(argv[1], "read", 4) == 0) ? NANDRW_READ : NANDRW_WRITE; int ret, total; char* cmdtail = strchr(argv[1], '.'); if (cmdtail && !strncmp(cmdtail, ".oob", 2)) { /* read out-of-band data */ if (cmd & NANDRW_READ) { ret = nand_read_oob(nand_dev_desc + curr_device, off, size, &total, (u_char*)addr); } else { ret = nand_write_oob(nand_dev_desc + curr_device, off, size, &total, (u_char*)addr); } return ret; } else if (cmdtail && !strncmp(cmdtail, ".jffs2", 2)) cmd |= NANDRW_JFFS2; /* skip bad blocks */ else if (cmdtail && !strncmp(cmdtail, ".jffs2s", 2)) { cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ if (cmd & NANDRW_READ) cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ }#ifdef SXNI855T /* need ".e" same as ".j" for compatibility with older units */ else if (cmdtail && !strcmp(cmdtail, ".e")) cmd |= NANDRW_JFFS2; /* skip bad blocks */#endif else if (cmdtail) { printf ("Usage:\n%s\n", cmdtp->usage); return 1; } printf ("\nNAND %s: device %d offset %ld, size %ld ... ", (cmd & NANDRW_READ) ? "read" : "write", curr_device, off, size); ret = nand_rw(nand_dev_desc + curr_device, cmd, off, size, &total, (u_char*)addr); printf (" %d bytes %s: %s\n", total, (cmd & NANDRW_READ) ? "read" : "written", ret ? "ERROR" : "OK"); return ret; } else if (strcmp(argv[1],"erase") == 0 && (argc == 4 || strcmp("clean", argv[2]) == 0)) { int clean = argc == 5; ulong off = simple_strtoul(argv[2 + clean], NULL, 16); ulong size = simple_strtoul(argv[3 + clean], NULL, 16); int ret; printf ("\nNAND erase: device %d offset %ld, size %ld ... ", curr_device, off, size); ret = nand_erase (nand_dev_desc + curr_device, off, size, clean); printf("%s\n", ret ? "ERROR" : "OK"); return ret; } else { printf ("Usage:\n%s\n", cmdtp->usage); rcode = 1; } return rcode; }}U_BOOT_CMD( nand, 5, 1, do_nand, "nand - NAND sub-system\n", "info - show available NAND devices\n" "nand device [dev] - show or set current device\n" "nand read[.jffs2[s]] addr off size\n" "nand write[.jffs2] addr off size - read/write `size' bytes starting\n" " at offset `off' to/from memory address `addr'\n" "nand erase [clean] [off size] - erase `size' bytes from\n" " offset `off' (entire device if not specified)\n" "nand bad - show bad blocks\n" "nand read.oob addr off size - read out-of-band data\n" "nand write.oob addr off size - read out-of-band data\n");int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){ char *boot_device = NULL; char *ep; int dev; ulong cnt; ulong addr; ulong offset = 0; image_header_t *hdr; int rcode = 0; switch (argc) { case 1: addr = CFG_LOAD_ADDR; boot_device = getenv ("bootdevice"); break; case 2: addr = simple_strtoul(argv[1], NULL, 16); boot_device = getenv ("bootdevice"); break; case 3: addr = simple_strtoul(argv[1], NULL, 16); boot_device = argv[2]; break; case 4: addr = simple_strtoul(argv[1], NULL, 16); boot_device = argv[2]; offset = simple_strtoul(argv[3], NULL, 16); break; default: printf ("Usage:\n%s\n", cmdtp->usage); SHOW_BOOT_PROGRESS (-1); return 1; } if (!boot_device) { puts ("\n** No boot device **\n"); SHOW_BOOT_PROGRESS (-1); return 1; } dev = simple_strtoul(boot_device, &ep, 16); if ((dev >= CFG_MAX_NAND_DEVICE) || (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN)) { printf ("\n** Device %d not available\n", dev); SHOW_BOOT_PROGRESS (-1); return 1; } printf ("\nLoading from device %d: %s at 0x%lx (offset 0x%lx)\n", dev, nand_dev_desc[dev].name, nand_dev_desc[dev].IO_ADDR, offset); if (nand_rw (nand_dev_desc + dev, NANDRW_READ, offset, SECTORSIZE, NULL, (u_char *)addr)) { printf ("** Read error on %d\n", dev); SHOW_BOOT_PROGRESS (-1); return 1; } hdr = (image_header_t *)addr; if (ntohl(hdr->ih_magic) == IH_MAGIC) { print_image_hdr (hdr); cnt = (ntohl(hdr->ih_size) + sizeof(image_header_t)); cnt -= SECTORSIZE; } else { printf ("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic); SHOW_BOOT_PROGRESS (-1); return 1; } if (nand_rw (nand_dev_desc + dev, NANDRW_READ, offset + SECTORSIZE, cnt, NULL, (u_char *)(addr+SECTORSIZE))) { printf ("** Read error on %d\n", dev); SHOW_BOOT_PROGRESS (-1); return 1; } /* Loading ok, update default load address */ load_addr = addr; /* Check if we should attempt an auto-start */ if (((ep = getenv("autostart")) != NULL) && (strcmp(ep,"yes") == 0)) { char *local_args[2]; extern int do_bootm (cmd_tbl_t *, int, int, char *[]); local_args[0] = argv[0]; local_args[1] = NULL; printf ("Automatic boot of image at addr 0x%08lx ...\n", addr); do_bootm (cmdtp, 0, 1, local_args); rcode = 1; } return rcode;}U_BOOT_CMD( nboot, 4, 1, do_nandboot, "nboot - boot from NAND device\n", "loadAddr dev\n");/* returns 0 if block containing pos is OK: * valid erase block and * not marked bad, or no bad mark position is specified * returns 1 if marked bad or otherwise invalid */int check_block(struct nand_chip* nand, unsigned long pos){ int retlen; uint8_t oob_data; int page0 = pos & (-nand->erasesize); int page1 = page0 + nand->oobblock; int badpos = oob_config.badblock_pos; if (pos >= nand->totlen) return 1; if (badpos < 0) return 0; /* no way to check, assume OK */ /* Note - bad block marker can be on first or second page */ if (nand_read_oob(nand, page0 + badpos, 1, &retlen, &oob_data) || oob_data != 0xff || nand_read_oob(nand, page1 + badpos, 1, &retlen, &oob_data) || oob_data != 0xff) return 1; return 0;}/* print bad blocks in NAND flash */static void nand_print_bad(struct nand_chip* nand){ unsigned long pos; for (pos = 0; pos < nand->totlen; pos += nand->erasesize) { if (check_block(nand, pos)) printf(" 0x%8.8lx\n", pos); } puts("\n");}/* cmd: 0: NANDRW_WRITE write, fail on bad block * 1: NANDRW_READ read, fail on bad block * 2: NANDRW_WRITE | NANDRW_JFFS2 write, skip bad blocks * 3: NANDRW_READ | NANDRW_JFFS2 read, data all 0xff for bad blocks * 7: NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP read, skip bad blocks */static int nand_rw (struct nand_chip* nand, int cmd, size_t start, size_t len, size_t * retlen, u_char * buf){ int ret = 0, n, total = 0; char eccbuf[6]; /* eblk (once set) is the start of the erase block containing the * data being processed. */ unsigned long eblk = ~0; /* force mismatch on first pass */ unsigned long erasesize = nand->erasesize; while (len) { if ((start & (-erasesize)) != eblk) { /* have crossed into new erase block, deal with * it if it is sure marked bad. */ eblk = start & (-erasesize); /* start of block */ if (check_block(nand, eblk)) { if (cmd == (NANDRW_READ | NANDRW_JFFS2)) { while (len > 0 && start - eblk < erasesize) { *(buf++) = 0xff; ++start; ++total; --len; } continue; } else if (cmd == (NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP)) { start += erasesize; continue; } else if (cmd == (NANDRW_WRITE | NANDRW_JFFS2)) { /* skip bad block */ start += erasesize; continue; } else { ret = 1; break; } } } /* The ECC will not be calculated correctly if less than 512 is written or read */ /* Is request at least 512 bytes AND it starts on a proper boundry */ if((start != ROUND_DOWN(start, 0x200)) || (len < 0x200)) printf("Warning block writes should be at least 512 bytes and start on a 512 byte boundry\n"); if (cmd & NANDRW_READ) ret = nand_read_ecc(nand, start, min(len, eblk + erasesize - start), &n, (u_char*)buf, eccbuf); else ret = nand_write_ecc(nand, start, min(len, eblk + erasesize - start), &n, (u_char*)buf, eccbuf); if (ret) break; start += n; buf += n; total += n; len -= n; } if (retlen) *retlen = total; return ret;}static void nand_print(struct nand_chip *nand){ if (nand->numchips > 1) { printf("%s at 0x%lx,\n" "\t %d chips %s, size %d MB, \n" "\t total size %ld MB, sector size %ld kB\n", nand->name, nand->IO_ADDR, nand->numchips, nand->chips_name, 1 << (nand->chipshift - 20), nand->totlen >> 20, nand->erasesize >> 10); } else { printf("%s at 0x%lx (", nand->chips_name, nand->IO_ADDR); print_size(nand->totlen, ", "); print_size(nand->erasesize, " sector)\n"); }}/* ------------------------------------------------------------------------- */static int NanD_WaitReady(struct nand_chip *nand, int ale_wait){ /* This is inline, to optimise the common case, where it's ready instantly */ int ret = 0;#ifdef NAND_NO_RB /* in config file, shorter delays currently wrap accesses */ if(ale_wait) NAND_WAIT_READY(nand); /* do the worst case 25us wait */ else udelay(10);#else /* has functional r/b signal */ NAND_WAIT_READY(nand);#endif return ret;}/* NanD_Command: Send a flash command to the flash chip */static inline int NanD_Command(struct nand_chip *nand, unsigned char command){ unsigned long nandptr = nand->IO_ADDR; /* Assert the CLE (Command Latch Enable) line to the flash chip */ NAND_CTL_SETCLE(nandptr); /* Send the command */ WRITE_NAND_COMMAND(command, nandptr); /* Lower the CLE line */ NAND_CTL_CLRCLE(nandptr);#ifdef NAND_NO_RB if(command == NAND_CMD_RESET){ u_char ret_val; NanD_Command(nand, NAND_CMD_STATUS); do{ ret_val = READ_NAND(nandptr);/* wait till ready */ } while((ret_val & 0x40) != 0x40); }#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -