📄 cmd_nand.c
字号:
/* * 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)#ifdef CONFIG_AT91RM9200DK#include <asm/arch/hardware.h>#endif#ifdef CONFIG_S3C2440#include <s3c2440.h>#endif#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#define NAND_DEBUG//#define 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 1#define CONFIG_MTD_NAND_ECC /* enable ECC */#define CONFIG_MTD_NAND_ECC_JFFS2/* bits for nand_rw() `cmd'; or together as needed */#define NANDRW_READ 0x01#define NANDRW_WRITE 0x00#define NANDRW_JFFS2 0x02#define NANDRW_JFFS2_SKIP 0x04/* * Function Prototypes */static void nand_print(struct nand_chip *nand);int nand_rw (struct nand_chip* nand, int cmd, size_t start, size_t len, size_t * retlen, u_char * buf);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); */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); */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#ifdef CFG_NAND_SKIP_BAD_DOT_I /* need ".i" same as ".jffs2s" for compatibility with older units (esd) */ /* ".i" for image -> read skips bad block (no 0xff) */ else if (cmdtail && !strcmp(cmdtail, ".i")) cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ if (cmd & NANDRW_READ) cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */#endif /* CFG_NAND_SKIP_BAD_DOT_I */ 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_block1(struct nand_chip* nand, unsigned long pos){ int retlen; uint8_t oob_data1; uint8_t oob_data2; int page0 = pos & (-nand->erasesize); int page1 = page0 + nand->oobblock; int badpos = oob_config.badblock_pos; int ret1; int ret2; 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 */ ret1 = nand_read_oob(nand, page0 + badpos, 1, &retlen, &oob_data1); ret2 = nand_read_oob(nand, page1 + badpos, 1, &retlen, &oob_data2); if(ret1 || ret2 || (oob_data1 != 0xff && oob_data2 != 0xff)) return 1; return 0;}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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -