From a718de3f42a6280fefd8b60b3e4733e015b0f637 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Mon, 11 Oct 2010 14:24:08 -0400 Subject: Additional NAND support --- board/omap3/logic/logic.c | 88 ++++++-- common/cmd_nand.c | 42 +++- cpu/arm_cortexa8/omap3/board.c | 19 +- drivers/mtd/mtdcore.c | 4 + drivers/mtd/nand/nand_base.c | 93 +++++++- drivers/mtd/nand/nand_util.c | 78 +++++-- drivers/mtd/nand/omap_gpmc.c | 383 ++++++++++++++++++++++++++++++++- include/asm-arm/arch-omap3/omap_gpmc.h | 19 ++ include/asm-arm/arch-omap3/sys_proto.h | 10 +- include/configs/omap3_logic.h | 50 +---- include/linux/mtd/mtd.h | 3 +- include/linux/mtd/nand.h | 12 ++ include/nand.h | 2 + lib_arm/board.c | 9 +- 14 files changed, 718 insertions(+), 94 deletions(-) diff --git a/board/omap3/logic/logic.c b/board/omap3/logic/logic.c index e0bcbc4bf3..77d5e21bbb 100644 --- a/board/omap3/logic/logic.c +++ b/board/omap3/logic/logic.c @@ -38,6 +38,8 @@ #include #include #include "logic.h" +#include "product_id.h" +#include /* * Routine: logic_identify @@ -47,7 +49,7 @@ */ unsigned int logic_identify(void) { - unsigned int val; + unsigned int val = 0; int i; MUX_LOGIC_HSUSB0_D5_GPIO_MUX(); @@ -64,19 +66,66 @@ unsigned int logic_identify(void) omap_set_gpio_direction(189, 1); val = omap_get_gpio_datain(189); omap_free_gpio(189); - } - printf("Board: "); - if (val) { - printf("Torpedo\n"); - val = MACH_TYPE_OMAP3_TORPEDO; - } else { - printf("LV SOM\n"); - val = MACH_TYPE_OMAP3530_LV_SOM; + printf("Board: "); + if (val) { + printf("Torpedo\n"); + val = MACH_TYPE_OMAP3_TORPEDO; + } else { + printf("LV SOM\n"); + val = MACH_TYPE_OMAP3530_LV_SOM; + } } return val; } + +#define LOGIC_NAND_GPMC_CONFIG1 0x00001800 +#define LOGIC_NAND_GPMC_CONFIG2 0x00090900 +#define LOGIC_NAND_GPMC_CONFIG3 0x00090902 +#define LOGIC_NAND_GPMC_CONFIG4 0x07020702 +#define LOGIC_NAND_GPMC_CONFIG5 0x00080909 +#define LOGIC_NAND_GPMC_CONFIG6 0x000002CF +#define LOGIC_NAND_GPMC_CONFIG7 0x00000C70 + +static void setup_nand_settings(void) +{ + /* Configure GPMC registers */ + writel(0x00000000, &gpmc_cfg->cs[0].config7); + sdelay(1000); + writel(LOGIC_NAND_GPMC_CONFIG1, &gpmc_cfg->cs[0].config1); + writel(LOGIC_NAND_GPMC_CONFIG2, &gpmc_cfg->cs[0].config2); + writel(LOGIC_NAND_GPMC_CONFIG3, &gpmc_cfg->cs[0].config3); + writel(LOGIC_NAND_GPMC_CONFIG4, &gpmc_cfg->cs[0].config4); + writel(LOGIC_NAND_GPMC_CONFIG5, &gpmc_cfg->cs[0].config5); + writel(LOGIC_NAND_GPMC_CONFIG6, &gpmc_cfg->cs[0].config6); + writel(LOGIC_NAND_GPMC_CONFIG7, &gpmc_cfg->cs[0].config7); + sdelay(2000); +} + +#define LOGIC_CF_GPMC_CONFIG1 0x00001210 +#define LOGIC_CF_GPMC_CONFIG2 0x00131000 +#define LOGIC_CF_GPMC_CONFIG3 0x001f1f01 +#define LOGIC_CF_GPMC_CONFIG4 0x10030e03 +#define LOGIC_CF_GPMC_CONFIG5 0x010f1411 +#define LOGIC_CF_GPMC_CONFIG6 0x80030600 +#define LOGIC_CF_GPMC_CONFIG7 0x00000f58 + +static void setup_cf_gpmc_setup(void) +{ + /* Configure GPMC registers */ + writel(0x00000000, &gpmc_cfg->cs[3].config7); + sdelay(1000); + writel(LOGIC_CF_GPMC_CONFIG1, &gpmc_cfg->cs[3].config1); + writel(LOGIC_CF_GPMC_CONFIG2, &gpmc_cfg->cs[3].config2); + writel(LOGIC_CF_GPMC_CONFIG3, &gpmc_cfg->cs[3].config3); + writel(LOGIC_CF_GPMC_CONFIG4, &gpmc_cfg->cs[3].config4); + writel(LOGIC_CF_GPMC_CONFIG5, &gpmc_cfg->cs[3].config5); + writel(LOGIC_CF_GPMC_CONFIG6, &gpmc_cfg->cs[3].config6); + writel(LOGIC_CF_GPMC_CONFIG7, &gpmc_cfg->cs[3].config7); + sdelay(2000); +} + /* * Routine: board_init * Description: Early hardware init. @@ -86,6 +135,15 @@ int board_init(void) DECLARE_GLOBAL_DATA_PTR; gpmc_init(); /* in SRAM or SDRAM, finish GPMC */ + + /* Update NAND settings */ + setup_nand_settings(); + +#if 0 + /* Update CF settings */ + setup_cf_gpmc_setup(); +#endif + /* board id for Linux (placeholder until can ID board) */ gd->bd->bi_arch_number = MACH_TYPE_OMAP3530_LV_SOM; /* boot param addr */ @@ -94,7 +152,6 @@ int board_init(void) return 0; } - static void setup_net_chip(void); static void setup_isp1760_chip(void); static void fix_flash_sync(void); @@ -196,6 +253,11 @@ int board_late_init(void) // Fetch the ethaddr of the WiFi board_get_nth_enetaddr(enetaddr, 1, 1); #endif + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + // Unlock the whole chip + nand_unlock(&nand_info[0], 0x0, nand_info[0].size); +#endif return 0; } @@ -212,11 +274,11 @@ void set_muxconf_regs(void) // GPMC settings for LV SOM Ethernet chip #define LOGIC_NET_GPMC_CONFIG1 0x00001000 -#define LOGIC_NET_GPMC_CONFIG2 0x00080802 +#define LOGIC_NET_GPMC_CONFIG2 0x00080701 #define LOGIC_NET_GPMC_CONFIG3 0x00000000 -#define LOGIC_NET_GPMC_CONFIG4 0x08020802 +#define LOGIC_NET_GPMC_CONFIG4 0x08010702 #define LOGIC_NET_GPMC_CONFIG5 0x00080a0a -#define LOGIC_NET_GPMC_CONFIG6 0x00000000 +#define LOGIC_NET_GPMC_CONFIG6 0x03000280 #define LOGIC_NET_GPMC_CONFIG7 0x00000f48 /* diff --git a/common/cmd_nand.c b/common/cmd_nand.c index d71fc49077..c3629f49d1 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -182,7 +182,11 @@ static void do_nand_status(nand_info_t *nand) int last_status = -1; struct nand_chip *nand_chip = nand->priv; - /* check the WP bit */ + + /* Check the WP bit. To do so requires resetting the device to + force the status back to its reset value (so WP becomes whether + the WP pin is set). */ + nand_chip->cmdfunc(nand, NAND_CMD_RESET, -1, -1); nand_chip->cmdfunc(nand, NAND_CMD_STATUS, -1, -1); printf("device is %swrite protected\n", (nand_chip->read_byte(nand) & 0x80 ? @@ -283,7 +287,8 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && - strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) + strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 && + strcmp(cmd, "debug") != 0 && strcmp(cmd, "features") != 0) goto usage; /* the following commands operate on the current device */ @@ -399,8 +404,12 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) opts.blockalign = 1; opts.quiet = quiet; opts.writeoob = 1; +#if 1 + /* Use current ecc layout */ +#else opts.autoplace = 1; opts.forceyaffs = 1; +#endif nand_write_opts(nand, &opts); } else if (!strcmp(s, ".oob")) { /* out-of-band data */ @@ -456,6 +465,26 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) return 1; } +#ifdef CONFIG_MTD_DEBUG + if (strcmp(cmd, "debug") == 0) { + if (argc == 3) { + ulong val = simple_strtoul(argv[2], NULL, 16); + mtd_debug_verbose = val; + } else + printf("%d\n", mtd_debug_verbose); + return 1; + } +#endif + + if (strcmp(cmd, "features") == 0) { + uint8_t features[5]; + addr = simple_strtoul(argv[2], NULL, 16); + nand_get_features(nand, addr, features); + printf("%02x %02x %02x %02x %02x\n", features[0], + features[1], features[2], features[3], features[4]); + return 1; + } + #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK if (strcmp(cmd, "lock") == 0) { int tight = 0; @@ -483,6 +512,8 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) return 1; + printf ("nand_unlock: start: %08x, length: %#x\n", + (int)off, (int)size); if (!nand_unlock(nand, off, size)) { puts("NAND flash successfully unlocked\n"); } else { @@ -515,13 +546,18 @@ U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand, "nand dump[.oob] off - dump page\n" "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" - "nand biterr off - make a bit error at offset (UNSAFE)" + "nand biterr off - make a bit error at offset (UNSAFE)\n" + "features addr - dump the features addr" #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK "\n" "nand lock [tight] [status]\n" " bring nand to lock state or display locked pages\n" "nand unlock [offset] [size] - unlock section" #endif +#ifdef CONFIG_MTD_DEBUG + "\n" + "nand debug [level] - display or set MTD debug level" +#endif ); static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, diff --git a/cpu/arm_cortexa8/omap3/board.c b/cpu/arm_cortexa8/omap3/board.c index 1c29e2143e..b8db9783b8 100644 --- a/cpu/arm_cortexa8/omap3/board.c +++ b/cpu/arm_cortexa8/omap3/board.c @@ -331,18 +331,27 @@ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) nand = mtd->priv; if (argc == 1) { - if (nand->ecc.mode == NAND_ECC_SOFT) + switch(nand->ecc.mode) { + case NAND_ECC_SOFT: printf("Software ECC\n"); - else + break; + case NAND_ECC_HW: printf("Hardware ECC\n"); + break; + case NAND_ECC_CHIP: + printf("Internal to NAND Hardware ECC\n"); + break; + } return 0; } if (argc != 2) goto usage; if (strncmp(argv[1], "hw", 2) == 0) - omap_nand_switch_ecc(1); + omap_nand_switch_ecc(OMAP_ECC_HW); else if (strncmp(argv[1], "sw", 2) == 0) - omap_nand_switch_ecc(0); + omap_nand_switch_ecc(OMAP_ECC_SOFT); + else if (strncmp(argv[1], "chip", 4) == 0) + omap_nand_switch_ecc(OMAP_ECC_CHIP); else goto usage; @@ -356,7 +365,7 @@ usage: U_BOOT_CMD( nandecc, 2, 1, do_switch_ecc, "nandecc - switch OMAP3 NAND ECC calculation algorithm", - "[hw/sw] - Switch between NAND hardware (hw) or software (sw) ecc algorithm" + "[hw/sw/chip] - Switch between NAND hardware (hw), software (sw), or chip (chip) ecc algorithm" ); #endif /* CONFIG_NAND_OMAP_GPMC */ diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6eb52ed50c..7bc62e6f86 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -11,6 +11,10 @@ #include #include +#ifdef CONFIG_MTD_DEBUG +int mtd_debug_verbose = CONFIG_MTD_DEBUG_VERBOSE; +#endif + struct mtd_info *mtd_table[MAX_MTD_DEVICES]; int add_mtd_device(struct mtd_info *mtd) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 360b070849..1f3a638748 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -454,11 +454,26 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) static int nand_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - /* Check the WP bit */ + + /* Check the WP bit. To do so requires resetting the device to + force the status back to its reset value (so WP becomes whether + the WP pin is set). */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } +static void nand_set_feature(struct mtd_info *mtd, int feature_address, uint8_t *params) +{ + struct nand_chip *chip = mtd->priv; + + chip->cmd_ctrl(mtd, NAND_CMD_SETFEATURE, NAND_CTRL_CLE); + chip->cmd_ctrl(mtd, feature_address, NAND_CTRL_ALE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_CTRL_CHANGE | NAND_NCE); + udelay(100); + chip->write_buf(mtd, params, 4); +} + /** * nand_block_checkbad - [GENERIC] Check if a block is marked bad * @mtd: MTD device structure @@ -1217,6 +1232,26 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, sndcmd = 0; } + /* If in chip ECC mode, need to read the status + to see if an ECC error occurred. */ + if (chip->ecc.mode == NAND_ECC_CHIP) { + int status; + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, + NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + status = chip->read_byte(mtd); + chip->cmd_ctrl(mtd, NAND_CMD_READ0, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + if (status & 0x1) + mtd->ecc_stats.failed++; + else if (status & 0x10) + mtd->ecc_stats.corrected++; + } + /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); @@ -1420,6 +1455,8 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); + if ((~status) & NAND_STATUS_WP) + return -EPERM; return status & NAND_STATUS_FAIL ? -EIO : 0; } @@ -1479,6 +1516,8 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); + if ((~status) & NAND_STATUS_WP) + return -EPERM; return status & NAND_STATUS_FAIL ? -EIO : 0; } @@ -1768,6 +1807,11 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, * See if operation failed and additional status checks are * available */ + if ((~status) & NAND_STATUS_WP) { + printf("%s: failed to write to write-protected page\n", __FUNCTION__); + return -EPERM; + } + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) status = chip->errstat(mtd, chip, FL_WRITING, status, page); @@ -1777,6 +1821,10 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, } else { chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); status = chip->waitfunc(mtd, chip); + if ((~status) & NAND_STATUS_WP) { + printf("%s: failed to write to write-protected page\n", __FUNCTION__); + return -EPERM; + } } #ifdef CONFIG_MTD_NAND_VERIFY_WRITE @@ -2005,7 +2053,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, /* Do not allow write past end of page */ if ((ops->ooboffs + ops->ooblen) > len) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Attempt to write past end of page\n"); + "Attempt to write past end of page (%d+%d>%d)\n", ops->ooboffs, ops->ooblen, len); return -EINVAL; } @@ -2249,7 +2297,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, chip->erase_cmd(mtd, page & chip->pagemask); status = chip->waitfunc(mtd, chip); - + /* * See if operation failed and additional status checks are * available @@ -2258,6 +2306,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, status = chip->errstat(mtd, chip, FL_ERASING, status, page); + /* If block is write-protected, skip to next */ + if ((~status) & NAND_STATUS_WP) { + printk(KERN_WARNING "nand_erase: attempt to erase a " + "write-protected block at page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + /* See if block erase succeeded */ if (status & NAND_STATUS_FAIL) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " @@ -2502,6 +2558,9 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, return ERR_PTR(-ENODEV); } + chip->maf_id = tmp_manf; + chip->dev_id = tmp_id; + /* Lookup the flash id */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (dev_id == nand_flash_ids[i].id) { @@ -2777,6 +2836,26 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = 256; chip->ecc.bytes = 3; + if (chip->has_chip_ecc) { + /* Put chip into no-ECC mode */ + uint8_t params[4] = {0x00, 0x00, 0x00, 0x00}; + nand_set_feature(mtd, 0x90, params); + } + break; + + case NAND_ECC_CHIP: + chip->ecc.read_page = nand_read_page_raw; + chip->ecc.write_page = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + chip->ecc.size = mtd->writesize; + chip->ecc.bytes = 0; + + if (chip->has_chip_ecc) { + /* Put chip into ECC mode */ + uint8_t params[4] = {0x08, 0x00, 0x00, 0x00}; + nand_set_feature(mtd, 0x90, params); + } break; case NAND_ECC_NONE: @@ -2788,6 +2867,13 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; + + if (chip->has_chip_ecc) { + /* Put chip into ECC mode */ + uint8_t params[4] = {0x00, 0x00, 0x00, 0x00}; + nand_set_feature(mtd, 0x90, params); + } + break; default: @@ -2805,6 +2891,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length; mtd->oobavail = chip->ecc.layout->oobavail; + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: oobavail %d\n", __FUNCTION__, mtd->oobavail); /* * Set the number of read / write steps for one page depending on ECC diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index c866c1210c..7db9b66985 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -339,6 +339,7 @@ int nand_lock(struct mtd_info *mtd, int tight) int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) { int ret = 0; + int flags = 0; int chipnr; int page; struct nand_chip *chip = mtd->priv; @@ -360,9 +361,19 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) page = (int)(offset >> chip->page_shift); chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); - ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT - | NAND_LOCK_STATUS_LOCK - | NAND_LOCK_STATUS_UNLOCK); + ret = chip->read_byte(mtd); + + ret &= (NAND_LOCK_STATUS_TIGHT | NAND_LOCK_STATUS_LOCK | NAND_LOCK_STATUS_UNLOCK); + + if (ret & NAND_LOCK_STATUS_TIGHT) + flags |= NAND_LOCK_STATUS_TIGHT; + + if (ret & NAND_LOCK_STATUS_UNLOCK) + flags |= NAND_LOCK_STATUS_UNLOCK; + else + flags |= NAND_LOCK_STATUS_LOCK; + + ret = flags; out: /* de-select the NAND device */ @@ -370,6 +381,32 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) return ret; } +int nand_get_features(struct mtd_info *mtd, uint8_t faddr, uint8_t *features) +{ + struct nand_chip *chip = mtd->priv; + int i; + + chip->select_chip(mtd, 0); + + /* Send the status command */ + chip->cmd_ctrl(mtd, NAND_CMD_GET_FEATURES, NAND_CTRL_CHANGE | NAND_CTRL_CLE); + + /* Send the feature address */ + chip->cmd_ctrl(mtd, faddr, NAND_CTRL_CHANGE | NAND_CTRL_ALE); + /* Switch to data access */ + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_CTRL_CHANGE | NAND_NCE); + + ndelay(100); + + for (i=0; i<5; ++i) + features[i] = chip->read_byte(mtd); + +#if 0 + printf("%s: %02x %02x %02x %02x %02x\n", __FUNCTION__, features[0], + features[1], features[2], features[3], features[4]); +#endif + return 0; +} /** * nand_unlock: - Unlock area of NAND pages * only one consecutive area can be unlocked at one time! @@ -388,13 +425,19 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length) int status; int page; struct nand_chip *chip = mtd->priv; +#if 0 printf ("nand_unlock: start: %08x, length: %d!\n", (int)start, (int)length); - +#endif /* select the NAND device */ chipnr = (int)(start >> chip->chip_shift); chip->select_chip(mtd, chipnr); + /* Check the WP bit. To do so requires resetting the device to + force the status back to its reset value (so WP becomes whether + the WP pin is set). */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + /* check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) { @@ -732,7 +775,11 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) .ooblen = meminfo->ecclayout->oobavail, .oobretlen = 0, .ooboffs = 0, +#if 1 + .datbuf = data_buf, +#else .datbuf = NULL, +#endif .oobbuf = oob_buf, }; @@ -744,19 +791,20 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) goto restoreoob; } imglen -= meminfo->oobsize; - } + } else { - /* write out the page data */ - result = meminfo->write(meminfo, - mtdoffset, - meminfo->writesize, - &written, - (unsigned char *) &data_buf); + /* write out the page data */ + result = meminfo->write(meminfo, + mtdoffset, + meminfo->writesize, + &written, + (unsigned char *) &data_buf); - if (result != 0) { - printf("writing NAND page at offset 0x%lx failed\n", - mtdoffset); - goto restoreoob; + if (result != 0) { + printf("writing NAND page at offset 0x%lx failed\n", + mtdoffset); + goto restoreoob; + } } imglen -= readlen; diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 5624b06677..9590ed920f 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -26,11 +26,13 @@ #include #include #include +#include #include #include static uint8_t cs; static struct nand_ecclayout hw_nand_oob = GPMC_NAND_HW_ECC_LAYOUT; +static struct nand_ecclayout chip_nand_oob = GPMC_NAND_CHIP_ECC_LAYOUT; /* * omap_nand_hwcontrol - Set the address pointers corretly for the @@ -158,6 +160,25 @@ static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat, return 0; } +static int omap_correct_chip_hwecc(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *chip; + + chip = mtd->priv; + + printf("%s: ecc_status %02x\n", __func__, chip->ecc_status); + /* We stored the read status in info->ecc_status in the read. + If bit 0 is set, then there was an uncorrectable ECC error. + If bit 3 is set, then there was a correctable error (up to + four bits of correction). */ + if (chip->ecc_status & 0x01) + return -1; + if (chip->ecc_status & 0x08) + return 4; + return 0; +} + /* * omap_calculate_ecc - Generate non-inverted ECC bytes. * @@ -194,6 +215,13 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, return 0; } +static int omap_calculate_chip_hwecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:\n", __func__); + return 0; +} + /* * omap_enable_ecc - This function enables the hardware ecc functionality * @mtd: MTD device structure @@ -226,14 +254,335 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) } } +static void omap_enable_chip_hwecc(struct mtd_info *mtd, int mode) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:\n", __func__); +} + +/* + * omap_nand_chip_has_ecc - return true if chip has internal ECC + */ +int omap_nand_chip_has_ecc(void) +{ + struct nand_chip *chip; + struct mtd_info *mtd; + int i; + uint8_t ident[5]; + + if (nand_curr_device < 0 || + nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + printf("Error: Can't switch ecc, no devices available\n"); + return 0; + } + + mtd = &nand_info[nand_curr_device]; + chip = mtd->priv; + +#if 1 + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Wait for the chip to get the ID ready */ + ndelay(100); + + for (i=0; i<2; ++i) + ident[i] = chip->read_byte(mtd); + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d %02x %02x\n", __FUNCTION__, __LINE__, ident[0], ident[1]); + if (ident[0] == NAND_MFR_MICRON) { + for (i=2; i<5; ++i) + ident[i] = chip->read_byte(mtd); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d %02x %02x %02x\n", __FUNCTION__, __LINE__, ident[2], ident[3], ident[4]); + if (ident[4] & 0x3) + chip->has_chip_ecc = 1; + } + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: has_chip_ecc %d\n", __FUNCTION__, chip->has_chip_ecc); +#else + if (nand->maf_id == NAND_MFR_MICRON) { + switch(nand->dev_id) { + case 0x2c: + case 0xdc: + case 0xcc: + case 0xac: + case 0xbc: + case 0xa3: + case 0xb3: + case 0xd3: + case 0xc3: + nand->has_chip_ecc = 1; + return 1; + default: + break; + } + } +#endif + return chip->has_chip_ecc; +} + +/* + * omap_nand_set_features - set the features in the chip + * @faddr - features address + * @features - array of byts to set as features + */ +static void micron_set_features(struct mtd_info *mtd, uint8_t faddr, uint8_t *features) +{ + struct nand_chip *chip; + + chip = mtd->priv; + + chip->select_chip(mtd, 0); + + /* Send the status command */ + omap_nand_hwcontrol(mtd, NAND_CMD_SET_FEATURES, NAND_CTRL_CHANGE | NAND_CTRL_CLE); + /* Send the feature address */ + omap_nand_hwcontrol(mtd, faddr, NAND_CTRL_CHANGE | NAND_CTRL_ALE); + /* Switch to data access */ + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, NAND_CTRL_CHANGE | NAND_NCE); + + ndelay(100); + if (chip->options & NAND_BUSWIDTH_16) { + uint16_t ftrs16[4]; + int i; + for (i=0; i<4; ++i) + ftrs16[i] = features[i]; + chip->write_buf(mtd, (uint8_t *)ftrs16, sizeof(ftrs16)); + } else + chip->write_buf(mtd, features, 4); + + udelay(2); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: faddr %02x [%02x %02x %02x %02x]\n", __FUNCTION__, faddr, features[0], features[1], features[2], features[3]); +} + +static void micron_set_chip_ecc(struct mtd_info *mtd, int enable) +{ + uint8_t params[4]; + + MTDDEBUG(MTD_DEBUG_LEVEL3,"%s:%d enable %d\n", __FUNCTION__, __LINE__, enable); + + memset(params, 0x00, sizeof(params)); + if (enable) + params[0] = 0x08; + micron_set_features(mtd, 0x90, params); + +#if 0 + micron_get_features(mtd, 0x90, params); +#endif + MTDDEBUG(MTD_DEBUG_LEVEL3,"%s: %02x %02x %02x %02x\n", __FUNCTION__, params[0], params[1], params[2], params[3]); +} + +/** + * nand_read_oob_std - [REPLACABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int omap_read_oob_chipecc(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + struct nand_chip *nand; + + nand = mtd->priv; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: page = %d, len = %i\n", + __func__, page, mtd->oobsize); + + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + + /* Send the status command */ + omap_nand_hwcontrol(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + /* Switch to data access */ + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + chip->ecc_status = chip->read_byte(mtd); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: ecc_status %02x\n", __func__, chip->ecc_status); + if (chip->ecc_status & (0x8|0x1)) { + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d page %d ecc_status %02x\n", __FUNCTION__, __LINE__, page, chip->ecc_status); + if (chip->ecc_status & 0x1) + mtd->ecc_stats.failed++; + else if (chip->ecc_status & 0x80) + mtd->ecc_stats.corrected += 4; + } + + /* Send the read prefix */ + omap_nand_hwcontrol(mtd, NAND_CMD_READ0, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + /* Switch to data access */ + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/** + * omap_nand_command_lp - Send command to NAND large page device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This is the version for the new large page + * devices We dont have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. + */ +static void omap_nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Command latch cycle */ + omap_nand_hwcontrol(mtd, command & 0xff, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + + if (column != -1 || page_addr != -1) { + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + omap_nand_hwcontrol(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + omap_nand_hwcontrol(mtd, column >> 8, ctrl); + } + if (page_addr != -1) { + omap_nand_hwcontrol(mtd, page_addr, ctrl); + omap_nand_hwcontrol(mtd, page_addr >> 8, + NAND_NCE | NAND_ALE); + /* One more address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) + omap_nand_hwcontrol(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); + } + } + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * program and erase have their own busy handlers + * status, sequential in, and deplete1 need no delay + */ + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + return; + + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(chip->chip_delay); + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + omap_nand_hwcontrol(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; + return; + + case NAND_CMD_RNDOUT: + /* No ready / busy check necessary */ + omap_nand_hwcontrol(mtd, NAND_CMD_RNDOUTSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + return; + + case NAND_CMD_READ0: + + /* Send the read start */ + omap_nand_hwcontrol(mtd, NAND_CMD_READSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + goto ready_exit; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + nand_wait_ready(mtd); + +ready_exit: + /* If the chip has internal ECC, then we need to read the status + to determin if there's an ECC error - capture it for handling by + omap_nand_correct_chip_hwecc() later */ + if (command == NAND_CMD_READ0) { + if (chip->has_chip_ecc) { + + /* Send the status command */ + omap_nand_hwcontrol(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + /* Switch to data access */ + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + chip->ecc_status = chip->read_byte(mtd); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: ecc_status %02x\n", __func__, chip->ecc_status); +#if 0 + if (chip->ecc_status & (0x8|0x1)) + printk("%s:%d page %d column %d ecc_status %02x\n", __FUNCTION__, __LINE__, page_addr, column, chip->ecc_status); +#endif + + /* Send the read prefix */ + omap_nand_hwcontrol(mtd, NAND_CMD_READ0, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + /* Switch to data access */ + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + } + } + +} + /* * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. * The default is to come up on s/w ecc * - * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc + * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc, 2 - chip ecc, * */ -void omap_nand_switch_ecc(int32_t hardware) +void omap_nand_switch_ecc(enum omap_nand_ecc_mode mode) { struct nand_chip *nand; struct mtd_info *mtd; @@ -260,7 +609,7 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = NULL; /* Setup the ecc configurations again */ - if (hardware) { + if (mode == OMAP_ECC_HW) { nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_nand_oob; nand->ecc.size = 512; @@ -269,12 +618,38 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.correct = omap_correct_data; nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); + if (nand->has_chip_ecc) + micron_set_chip_ecc(mtd, 0); printf("NAND: HW ECC selected\n"); - } else { + } else if (mode == OMAP_ECC_SOFT) { nand->ecc.mode = NAND_ECC_SOFT; /* Use mtd default settings */ nand->ecc.layout = NULL; printf("NAND: SW ECC selected\n"); + if (nand->has_chip_ecc) + micron_set_chip_ecc(mtd, 0); + } else if (mode == OMAP_ECC_CHIP) { + if (!nand->has_chip_ecc) { + printf("NAND: Chip does not have internal ECC!\n"); + return; + } + nand->ecc.bytes = 0; + nand->ecc.size = 2048; + nand->ecc.calculate = omap_calculate_chip_hwecc; + nand->ecc.hwctl = omap_enable_chip_hwecc; + nand->ecc.correct = omap_correct_chip_hwecc; + nand->ecc.read_oob = omap_read_oob_chipecc; + nand->ecc.mode = NAND_ECC_CHIP; /* internal to chip */ + nand->ecc.layout = &chip_nand_oob; + if (nand->options & NAND_BUSWIDTH_16) + nand->cmdfunc = omap_nand_command_lp; + else + printf("%s: Huh? not 16-bit wide\n", __FUNCTION__); + micron_set_chip_ecc(mtd, 1); + printf("NAND: Internal to NAND ECC selected\n"); + } else { + printf("NAND: unknown ECC mode %d\n", mode); + return; } /* Update NAND handling after ECC mode switch */ diff --git a/include/asm-arm/arch-omap3/omap_gpmc.h b/include/asm-arm/arch-omap3/omap_gpmc.h index bd22bce837..c86448baa8 100644 --- a/include/asm-arm/arch-omap3/omap_gpmc.h +++ b/include/asm-arm/arch-omap3/omap_gpmc.h @@ -58,6 +58,25 @@ } #endif +/* Micron MT29F4G16ABBDA internal-to-NAND ECC layout */ +#define GPMC_NAND_CHIP_ECC_LAYOUT {\ + .eccbytes = 32,\ + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15, \ + 24, 25, 26, 27, 28, 19, 30, 31, \ + 40, 41, 42, 43, 44, 45, 46, 47, \ + 56, 57, 58, 59, 60, 61, 62, 63}, \ + .oobfree = {\ + {.offset = 4,\ + .length = 4 },\ + {.offset = 20,\ + .length = 4 },\ + {.offset = 36,\ + .length = 4 },\ + {.offset = 52,\ + .length = 4 },\ + } \ +}; + /* Small Page x8 NAND device Layout */ #ifdef GPMC_NAND_ECC_SP_x8_LAYOUT #define GPMC_NAND_HW_ECC_LAYOUT {\ diff --git a/include/asm-arm/arch-omap3/sys_proto.h b/include/asm-arm/arch-omap3/sys_proto.h index 7361d08961..a7a6523a42 100644 --- a/include/asm-arm/arch-omap3/sys_proto.h +++ b/include/asm-arm/arch-omap3/sys_proto.h @@ -60,7 +60,15 @@ void sr32(void *, u32, u32, u32); u32 wait_on_value(u32, u32, void *, u32); void sdelay(unsigned long); void make_cs1_contiguous(void); -void omap_nand_switch_ecc(int); + +enum omap_nand_ecc_mode { + OMAP_ECC_SOFT = 1, + OMAP_ECC_HW, + OMAP_ECC_CHIP, +}; + +void omap_nand_switch_ecc(enum omap_nand_ecc_mode mode); +int omap_nand_chip_has_ecc(void); void power_init_r(void); void dieid_num_r(void); diff --git a/include/configs/omap3_logic.h b/include/configs/omap3_logic.h index 1599eeea28..6dcec746d1 100644 --- a/include/configs/omap3_logic.h +++ b/include/configs/omap3_logic.h @@ -114,6 +114,7 @@ #define CONFIG_CMD_I2C /* I2C serial bus support */ #define CONFIG_CMD_MMC /* MMC support */ #define CONFIG_CMD_NAND /* NAND support */ +#define CONFIG_CMD_NAND_LOCK_UNLOCK #undef CONFIG_CMD_FLASH /* flinfo, erase, protect */ #undef CONFIG_CMD_FPGA /* FPGA configuration Support */ @@ -164,7 +165,6 @@ /* Environment information */ #define CONFIG_BOOTDELAY 10 -#if 1 #define CONFIG_EXTRA_ENV_SETTINGS \ "display=15\0" \ "loadaddr=0x81000000\0" \ @@ -177,55 +177,13 @@ "nfsboot=setenv bootargs display=${display} console=${consoledev},${baudrate} root=/dev/nfs rw nfsroot=${serverip}:${rootpath}${nfsoptions} ip=dhcp ${otherbootargs};tftpboot ${loadaddr} ${kernelimage};bootm ${loadaddr}\0" \ "ramboot=setenv bootargs display=${display} console=${consoledev},${baudrate} root=/dev/ram rw ramdisk_size=${ramdisksize} ${otherbootargs};tftpboot ${loadaddr} ${kernelimage};tftpboot ${rootfsaddr} rootfs.ext2.gz.uboot;bootm ${loadaddr} ${rootfsaddr}\0" \ "xipboot=setenv bootargs display=${display} console=${consoledev},${baudrate} root=/dev/ram rw ramdisk_size=${ramdisksize} ${otherbootargs};bootm ${loadaddr} ${rootfsaddr}\0" \ - "rootdevice=/dev/mtdblock4\0" \ + "rootdevice=/dev/mtdblock3\0" \ "rootfstype=yaffs\0" \ "mtdboot=setenv bootargs display=${display} console=${consoledev},${baudrate} root=${rootdevice} rootfstype=${rootfstype} rw ${otherbootargs};bootm ${loadaddr}\0" \ "sdmtdboot=setenv bootargs display=${display} console=${consoledev},${baudrate} root=${rootdevice} rootfstype=${rootfstype} rw ${otherbootargs};mmcinit;fatload mmc0 ${loadaddr} ${kernelimage}; bootm ${loadaddr}\0" #define CONFIG_BOOTCOMMAND "run xipboot" -#else - -// Beagle ENV_SETTINGS -#define CONFIG_EXTRA_ENV_SETTINGS \ - "loadaddr=0x82000000\0" \ - "console=ttyS2,115200n8\0" \ - "videomode=1024x768@60,vxres=1024,vyres=768\0" \ - "videospec=omapfb:vram:2M,vram:4M\0" \ - "mmcargs=setenv bootargs console=${console} " \ - "video=${videospec},mode:${videomode} " \ - "root=/dev/mmcblk0p2 rw " \ - "rootfstype=ext3 rootwait\0" \ - "nandargs=setenv bootargs console=${console} " \ - "video=${videospec},mode:${videomode} " \ - "root=/dev/mtdblock4 rw " \ - "rootfstype=jffs2\0" \ - "loadbootscript=fatload mmc 0 ${loadaddr} boot.scr\0" \ - "bootscript=echo Running bootscript from mmc ...; " \ - "source ${loadaddr}\0" \ - "loaduimage=fatload mmc 0 ${loadaddr} uImage\0" \ - "mmcboot=echo Booting from mmc ...; " \ - "run mmcargs; " \ - "bootm ${loadaddr}\0" \ - "nandboot=echo Booting from nand ...; " \ - "run nandargs; " \ - "nand read ${loadaddr} 280000 400000; " \ - "bootm ${loadaddr}\0" \ - -// Beagle BOOTCOMMAND -#define CONFIG_BOOTCOMMAND \ - "if mmc init; then " \ - "if run loadbootscript; then " \ - "run bootscript; " \ - "else " \ - "if run loaduimage; then " \ - "run mmcboot; " \ - "else run nandboot; " \ - "fi; " \ - "fi; " \ - "else run nandboot; fi" -#endif - #define CONFIG_PREBOOT \ "echo ======================NOTICE============================;" \ "echo This is the first time that you boot up this board. You are;" \ @@ -330,10 +288,8 @@ #define CONFIG_ENV_OFFSET boot_flash_off #define CONFIG_ENV_ADDR SMNAND_ENV_OFFSET -#if 0 #define CONFIG_MTD_DEBUG 1 -#define CONFIG_MTD_DEBUG_VERBOSE 2 // Loud MTD debug messages -#endif +#define CONFIG_MTD_DEBUG_VERBOSE -1 // No MTD debug messages /*----------------------------------------------------------------------- * CFI FLASH driver setup diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 16556c4798..11c76dc008 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -297,9 +297,10 @@ static inline void mtd_erase_callback(struct erase_info *instr) #define MTD_DEBUG_LEVEL3 (3) /* Noisy */ #ifdef CONFIG_MTD_DEBUG +extern int mtd_debug_verbose; #define MTDDEBUG(n, args...) \ do { \ - if (n <= CONFIG_MTD_DEBUG_VERBOSE) \ + if (n <= mtd_debug_verbose) \ printk(KERN_INFO args); \ } while(0) #else /* CONFIG_MTD_DEBUG */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 3e0044b94f..c23c1fd4cc 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -85,6 +85,8 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_RNDIN 0x85 #define NAND_CMD_READID 0x90 #define NAND_CMD_ERASE2 0xd0 +#define NAND_CMD_SETFEATURE 0xee +#define NAND_CMD_GETFEATURE 0xef #define NAND_CMD_RESET 0xff /* Extended commands for large page devices */ @@ -92,6 +94,11 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_RNDOUTSTART 0xE0 #define NAND_CMD_CACHEDPROG 0x15 +/* Extended commands for ONFI devices */ +#define NAND_CMD_READ_PARAM 0xec +#define NAND_CMD_GET_FEATURES 0xee +#define NAND_CMD_SET_FEATURES 0xef + /* Extended commands for AG-AND device */ /* * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but @@ -128,6 +135,7 @@ typedef enum { NAND_ECC_SOFT, NAND_ECC_HW, NAND_ECC_HW_SYNDROME, + NAND_ECC_CHIP, } nand_ecc_modes_t; /* @@ -369,6 +377,10 @@ struct nand_chip { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; + uint8_t maf_id, dev_id; /* manufacturer/device identifier */ + uint8_t has_chip_ecc; /* !0 if chip has internal ECC engine */ + uint8_t ecc_status; /* status of read w/ECC */ + uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); diff --git a/include/nand.h b/include/nand.h index 2a81597a65..03bbce9d5f 100644 --- a/include/nand.h +++ b/include/nand.h @@ -123,6 +123,8 @@ int nand_lock( nand_info_t *meminfo, int tight ); int nand_unlock( nand_info_t *meminfo, ulong start, ulong length ); int nand_get_lock_status(nand_info_t *meminfo, loff_t offset); +int nand_get_features( nand_info_t *meminfo, uint8_t faddr, uint8_t *features); + #ifdef CONFIG_SYS_NAND_SELECT_DEVICE void board_nand_select_device(struct nand_chip *nand, int chip); #endif diff --git a/lib_arm/board.c b/lib_arm/board.c index f984f6d60d..e9ccb9bec2 100644 --- a/lib_arm/board.c +++ b/lib_arm/board.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -363,9 +364,13 @@ void start_armboot (void) #if defined(CONFIG_CMD_NAND) puts ("NAND: "); nand_init(); /* go init the NAND */ -#endif #if defined(CONFIG_OMAP3_LOGIC) - omap_nand_switch_ecc(1); /* switch to HW ECC mode */ + if (omap_nand_chip_has_ecc()) { + omap_nand_switch_ecc(OMAP_ECC_CHIP); /* Use the chip's ECC */ + } else { + omap_nand_switch_ecc(OMAP_ECC_HW); /* switch to HW ECC mode */ + } +#endif #endif #if defined(CONFIG_CMD_ONENAND) -- cgit v1.2.3