diff options
author | Justin Waters <justin.waters@timesys.com> | 2012-04-17 13:43:17 -0400 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2012-04-17 13:43:17 -0400 |
commit | 4f60d7e7027af17ceffc1a38e6dbe4e3e95c71ec (patch) | |
tree | dd33f3760e08226d5c05036d664d2d68fb3765dc /drivers/mtd | |
parent | b1af6f532e0d348b153d5c148369229d24af361a (diff) |
LogicPD Support for OMAP3/DM3/AM3 boards
From Logic BSP-2.0-5-01
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/mtd_debug.c | 18 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 239 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bch.c | 287 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_util.c | 32 | ||||
-rw-r--r-- | drivers/mtd/nand/omap_gpmc.c | 425 |
7 files changed, 975 insertions, 28 deletions
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 5a5ecdfe3c6..f01f157c155 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -25,6 +25,7 @@ include $(TOPDIR)/config.mk LIB := $(obj)libmtd.o +COBJS-$(CONFIG_MTD_DEBUG) += mtd_debug.o COBJS-$(CONFIG_MTD_DEVICE) += mtdcore.o COBJS-$(CONFIG_MTD_PARTITIONS) += mtdpart.o COBJS-$(CONFIG_MTD_CONCAT) += mtdconcat.o diff --git a/drivers/mtd/mtd_debug.c b/drivers/mtd/mtd_debug.c new file mode 100644 index 00000000000..31913f05659 --- /dev/null +++ b/drivers/mtd/mtd_debug.c @@ -0,0 +1,18 @@ +/* + * MTD verbose debug + * + * (C) 2011 Peter Barada <peter.barada@logicpd.com> + * + * This code is GPL + */ + +#include <linux/mtd/mtd.h> +#include <linux/mtd/compat.h> +#include <ubi_uboot.h> + +#ifdef CONFIG_MTD_DEBUG +#ifndef CONFIG_MTD_DEBUG_VERBOSE +#define CONFIG_MTD_DEBUG_VERBOSE -1 +#endif +int mtd_debug_verbose = CONFIG_MTD_DEBUG_VERBOSE; +#endif diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 8b598f6bfef..975d152e794 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -50,6 +50,7 @@ COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o +COBJS-$(CONFIG_MTD_NAND_ECC_BCH) += nand_bch.o endif COBJS := $(COBJS-y) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 52f8575aac6..1092a797321 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -43,6 +43,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_bch.h> #ifdef CONFIG_MTD_PARTITIONS #include <linux/mtd/partitions.h> @@ -136,7 +137,11 @@ static void nand_release_device (struct mtd_info *mtd) static uint8_t nand_read_byte(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - return readb(chip->IO_ADDR_R); + uint8_t byte; + + byte = readb(chip->IO_ADDR_R); + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %02x\n", byte); + return byte; } /** @@ -149,7 +154,11 @@ static uint8_t nand_read_byte(struct mtd_info *mtd) static uint8_t nand_read_byte16(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); + uint8_t byte; + + byte = (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %02x\n", byte); + return byte; } /** @@ -162,7 +171,11 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd) static u16 nand_read_word(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - return readw(chip->IO_ADDR_R); + u16 word; + + word = readw(chip->IO_ADDR_R); + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %04x\n", word); + return word; } /** @@ -201,6 +214,8 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) int i; struct nand_chip *chip = mtd->priv; + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND W %d bytes\n", len); + for (i = 0; i < len; i++) writeb(buf[i], chip->IO_ADDR_W); } @@ -218,6 +233,8 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) int i; struct nand_chip *chip = mtd->priv; + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %d bytes\n", len); + for (i = 0; i < len; i++) buf[i] = readb(chip->IO_ADDR_R); } @@ -236,8 +253,10 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) struct nand_chip *chip = mtd->priv; for (i = 0; i < len; i++) - if (buf[i] != readb(chip->IO_ADDR_R)) + if (buf[i] != readb(chip->IO_ADDR_R)) { + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND verify fail at offset %d\n", i); return -EFAULT; + } return 0; } @@ -256,6 +275,8 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) u16 *p = (u16 *) buf; len >>= 1; + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND W %d words\n", len); + for (i = 0; i < len; i++) writew(p[i], chip->IO_ADDR_W); @@ -276,8 +297,20 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) u16 *p = (u16 *) buf; len >>= 1; - for (i = 0; i < len; i++) - p[i] = readw(chip->IO_ADDR_R); + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %d words\n", len); + + for (i = 0; i < len; i+=8) { + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + } + for (; i < len; ++i) + *p++ = readw(chip->IO_ADDR_R); } /** @@ -399,11 +432,88 @@ 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); + + /* Now check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } +/* + * nand_get_features: - Read the features of the NAND chip + * @param mtd nand mtd instance + * @param faddr nand feature address + * @param features return 4-byte array of features + * + * @return 0 on success, -1 in case of errors + */ +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<4; ++i) + features[i] = chip->read_byte(mtd); + + MTDDEBUG(MTD_DEBUG_LEVEL4, "%s: %02x %02x %02x %02x\n", __FUNCTION__, features[0], + features[1], features[2], features[3]); + + return 0; +} + +/** + * nand_set_features - [GENERIC] set features array + * @mtd: MTD device structure + * @faddr: feature address + * @params: 4-byte array of parameters to write + */ +int nand_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 */ + chip->cmd_ctrl(mtd, NAND_CMD_SET_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); + 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]); + + return 0; +} + /** * nand_block_checkbad - [GENERIC] Check if a block is marked bad * @mtd: MTD device structure @@ -751,7 +861,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) * * Not for syndrome calculating ecc controllers, which use a special oob layout */ -static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { chip->read_buf(mtd, buf, mtd->writesize); @@ -808,7 +918,7 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *c * @buf: buffer to store read data * @page: page number to read */ -static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { int i, eccsize = chip->ecc.size; @@ -850,7 +960,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @readlen: data length * @bufpoi: buffer to store read data */ -static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) +/* static */ int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) { int start_step, end_step, num_steps; uint32_t *eccpos = chip->ecc.layout->eccpos; @@ -1170,6 +1280,26 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, sndcmd = 0; } + /* If in chipe 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, @@ -1306,7 +1436,7 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, * @page: page number to read * @sndcmd: flag whether to issue read command or not */ -static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { if (sndcmd) { @@ -1362,7 +1492,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @page: page number to write */ -static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { int status = 0; @@ -1586,7 +1716,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, * * Not for syndrome calculating ecc controllers, which use a special oob layout */ -static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { chip->write_buf(mtd, buf, mtd->writesize); @@ -1637,7 +1767,7 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip * @chip: nand chip info structure * @buf: data buffer */ -static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { int i, eccsize = chip->ecc.size; @@ -1996,7 +2126,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; } @@ -2585,6 +2715,9 @@ static const 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; + if (!type) type = nand_flash_ids; @@ -2763,7 +2896,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* * If no default placement scheme is given, select an appropriate one */ - if (!chip->ecc.layout) { + if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: chip->ecc.layout = &nand_oob_8; @@ -2862,6 +2995,71 @@ 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_features(mtd, 0x90, params); + } + break; + + case NAND_ECC_SOFT_BCH: + printk(KERN_INFO "NAND ECC: SOFT_BCH\n"); + if (!mtd_nand_has_bch()) { + printk(KERN_WARNING "CONFIG_MTD_ECC_BCH not enabled\n"); + BUG(); + } + chip->ecc.calculate = nand_bch_calculate_ecc; + chip->ecc.correct = nand_bch_correct_data; + chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.read_subpage = nand_read_subpage; + chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + /* + * Board driver should supply ecc.size and ecc.bytes values to + * select how many bits are correctable; see nand_bch_init() + * for details. + * Otherwise, default to 4 bits for large page devices + */ + if (!chip->ecc.size && (mtd->oobsize >= 64)) { + chip->ecc.size = 512; + chip->ecc.bytes = 7; + } + chip->ecc.priv = nand_bch_init(mtd, + chip->ecc.size, + chip->ecc.bytes, + &chip->ecc.layout); + if (!chip->ecc.priv) { + printk(KERN_WARNING "BCH ECC initialization failed!\n"); + BUG(); + } + + if (chip->has_chip_ecc) { + /* Put chip into no-ECC mode */ + uint8_t params[4] = {0x00, 0x00, 0x00, 0x00}; + nand_set_features(mtd, 0x90, params); + } + break; + + case NAND_ECC_CHIP: + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw; + 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_features(mtd, 0x90, params); + } break; case NAND_ECC_NONE: @@ -2875,6 +3073,11 @@ 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] = {0x08, 0x00, 0x00, 0x00}; + nand_set_features(mtd, 0x90, params); + } break; default: @@ -2893,6 +3096,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 @@ -2994,6 +3198,9 @@ void nand_release(struct mtd_info *mtd) del_mtd_partitions(mtd); #endif + if (chip->ecc.mode == NAND_ECC_SOFT_BCH) + nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + /* Free bad block table memory */ kfree(chip->bbt); if (!(chip->options & NAND_OWN_BUFFERS)) diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c new file mode 100644 index 00000000000..632dc7ae7c1 --- /dev/null +++ b/drivers/mtd/nand/nand_bch.c @@ -0,0 +1,287 @@ +/* + * This file provides ECC correction for more than 1 bit per block of data, + * using binary BCH codes. It relies on the generic BCH library lib/bch.c. + * + * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. + * + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#if 1 +#if 1 +#include <config.h> +#include <common.h> +#include <linux/mtd/compat.h> +#include <malloc.h> +#endif +#include <linux/types.h> +// #include <linux/kernel.h> +// #include <linux/module.h> +// #include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_bch.h> +#include <linux/bch.h> +#else +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_bch.h> +#include <linux/bch.h> +#endif + +/** + * struct nand_bch_control - private NAND BCH control structure + * @bch: BCH control structure + * @ecclayout: private ecc layout for this BCH configuration + * @errloc: error location array + * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid + */ +struct nand_bch_control { + struct bch_control *bch; + struct nand_ecclayout ecclayout; + unsigned int *errloc; + unsigned char *eccmask; +}; + +/** + * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int i; + +#ifdef DEBUG_BCH + printf("%s:%d buf %p code %p\n", __FUNCTION__, __LINE__, buf, code); + for (i=0; i<16; ++i) + printf("%s%02x", !i?"dat: ":" ", buf[i]); + printf("\n"); +#endif + memset(code, 0, chip->ecc.bytes); + encode_bch(nbc->bch, buf, chip->ecc.size, code); + + /* apply mask so that an erased page is a valid codeword */ + for (i = 0; i < chip->ecc.bytes; i++) + code[i] ^= nbc->eccmask[i]; + +#ifdef DEBUG_BCH + printf("%s:%d code:", __FUNCTION__, __LINE__); + for (i=0; i<7; ++i) + printf(" %02x", code[i]); + printf("\n"); +#endif + + return 0; +} +// EXPORT_SYMBOL(nand_bch_calculate_ecc); + +/** + * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct bit errors for a data byte block + */ +int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int *errloc = nbc->errloc; + int i, count; + +#ifdef DEBUG_BCH + printf("%s:%d buf %p read_ecc %p calc_ecc %p\n", __FUNCTION__, __LINE__, buf, read_ecc, calc_ecc); + for (i=0; i<16; ++i) + printf("%s%02x", !i?"dat: ":" ", buf[i]); + printf("\n"); + for (i=0; i<7; ++i) + printf("%s%02x", !i?"read_ecc: ":" ", read_ecc[i]); + printf("\n"); + for (i=0; i<7; ++i) + printf("%s%02x", !i?"calc_ecc: ":" ", calc_ecc[i]); + printf("\n"); +#endif + + count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, + NULL, errloc); + if (count > 0) { + for (i = 0; i < count; i++) { + if (errloc[i] < (chip->ecc.size*8)) + /* error is located in data, correct it */ + buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); + /* else error in ecc, no action needed */ + + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: corrected bitflip %u\n", + __func__, errloc[i]); + } + } else if (count < 0) { + printk(KERN_ERR "ecc unrecoverable error\n"); + count = -1; + } + return count; +} +// EXPORT_SYMBOL(nand_bch_correct_data); + +/** + * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction + * @mtd: MTD block structure + * @eccsize: ecc block size in bytes + * @eccbytes: ecc length in bytes + * @ecclayout: output default layout + * + * Returns: + * a pointer to a new NAND BCH control structure, or NULL upon failure + * + * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes + * are used to compute BCH parameters m (Galois field order) and t (error + * correction capability). @eccbytes should be equal to the number of bytes + * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. + * + * Example: to configure 4 bit correction per 512 bytes, you should pass + * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) + * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) + */ +struct nand_bch_control * +nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, + struct nand_ecclayout **ecclayout) +{ + unsigned int m, t, eccsteps, i; + struct nand_ecclayout *layout; + struct nand_bch_control *nbc = NULL; + unsigned char *erased_page; + + if (!eccsize || !eccbytes) { + printk(KERN_WARNING "ecc parameters not supplied\n"); + goto fail; + } + + m = fls(1+8*eccsize); + t = (eccbytes*8)/m; + + nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); + if (!nbc) + goto fail; + + nbc->bch = init_bch(m, t, 0); + if (!nbc->bch) + goto fail; + + /* verify that eccbytes has the expected value */ + if (nbc->bch->ecc_bytes != eccbytes) { + printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", + eccbytes, nbc->bch->ecc_bytes); + goto fail; + } + + eccsteps = mtd->writesize/eccsize; + + /* if no ecc placement scheme was provided, build one */ + if (!*ecclayout) { + + /* handle large page devices only */ + if (mtd->oobsize < 64) { + printk(KERN_WARNING "must provide an oob scheme for " + "oobsize %d\n", mtd->oobsize); + goto fail; + } + + layout = &nbc->ecclayout; + layout->eccbytes = eccsteps*eccbytes; + + /* reserve 2 bytes for bad block marker */ + if (layout->eccbytes+2 > mtd->oobsize) { + printk(KERN_WARNING "no suitable oob scheme available " + "for oobsize %d eccbytes %u\n", mtd->oobsize, + eccbytes); + goto fail; + } + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; + + *ecclayout = layout; + } + + /* sanity checks */ + if (8*(eccsize+eccbytes) >= (1 << m)) { + printk(KERN_WARNING "eccsize %u is too large\n", eccsize); + goto fail; + } + if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) { + printk(KERN_WARNING "invalid ecc layout\n"); + goto fail; + } + + nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); + nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); + if (!nbc->eccmask || !nbc->errloc) + goto fail; + /* + * compute and store the inverted ecc of an erased ecc block + */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) + goto fail; + + memset(erased_page, 0xff, eccsize); + memset(nbc->eccmask, 0, eccbytes); + encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + nbc->eccmask[i] ^= 0xff; + + return nbc; +fail: + nand_bch_free(nbc); + return NULL; +} +// EXPORT_SYMBOL(nand_bch_init); + +/** + * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources + * @nbc: NAND BCH control structure + */ +void nand_bch_free(struct nand_bch_control *nbc) +{ + if (nbc) { + free_bch(nbc->bch); + kfree(nbc->errloc); + kfree(nbc->eccmask); + kfree(nbc); + } +} +// EXPORT_SYMBOL(nand_bch_free); + +// MODULE_LICENSE("GPL"); +// MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); +// MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 5a6f7aec888..82e661d39c9 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -120,6 +120,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) priv_nand->bbt = NULL; } + lcd_percent_init(erase_length); + for (erased_length = 0; erased_length < erase_length; erase.addr += meminfo->erasesize) { @@ -138,6 +140,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) if (!opts->spread) erased_length++; + lcd_percent_update(erased_length); + continue; } else if (ret < 0) { @@ -157,6 +161,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) continue; } + /* format for JFFS2 ? */ if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) { chip->ops.ooblen = 8; @@ -197,10 +202,13 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) erase.addr); } } + lcd_percent_update(erased_length); } + lcd_percent_update(erased_length); if (!opts->quiet) printf("\n"); + if (nand_block_bad_old) { struct nand_chip *priv_nand = meminfo->priv; @@ -337,8 +345,11 @@ 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); @@ -455,7 +466,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, u_char *buffer, int withoob) { int rval = 0, blocksize; - size_t left_to_write = *length; + size_t left_to_write = *length, total_to_write; u_char *p_buffer = buffer; int need_skip; @@ -499,7 +510,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, return -EINVAL; } - if (!need_skip) { + if (!need_skip && !withoob) { rval = nand_write (nand, offset, length, buffer); if (rval == 0) return 0; @@ -510,6 +521,9 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, return rval; } + total_to_write = left_to_write; + lcd_percent_init(total_to_write); + while (left_to_write > 0) { size_t block_offset = offset & (nand->erasesize - 1); size_t write_size; @@ -548,7 +562,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, ops.oobbuf = ops.datbuf + pagesize; rval = nand->write_oob(nand, offset, &ops); - if (!rval) + if (rval) break; offset += pagesize; @@ -571,8 +585,11 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, } left_to_write -= write_size; + lcd_percent_update(total_to_write - left_to_write); } + lcd_percent_update(total_to_write); + return 0; } @@ -594,7 +611,7 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, u_char *buffer) { int rval; - size_t left_to_read = *length; + size_t left_to_read = *length, total_to_read; u_char *p_buffer = buffer; int need_skip; @@ -622,6 +639,9 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, return rval; } + total_to_read = left_to_read; + lcd_percent_init(total_to_read); + while (left_to_read > 0) { size_t block_offset = offset & (nand->erasesize - 1); size_t read_length; @@ -651,7 +671,11 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, left_to_read -= read_length; offset += read_length; p_buffer += read_length; + + lcd_percent_update(total_to_read - left_to_read); } + lcd_percent_update(total_to_read); + return 0; } diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 99b9cef17c2..d5ee9bc5836 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -26,11 +26,15 @@ #include <asm/errno.h> #include <asm/arch/mem.h> #include <asm/arch/omap_gpmc.h> +#include <asm/arch/sys_proto.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_bch.h> #include <nand.h> 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 @@ -57,8 +61,17 @@ static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd, break; } - if (cmd != NAND_CMD_NONE) + if (cmd != NAND_CMD_NONE) { +#ifdef CONFIG_MTD_DEBUG + if (this->IO_ADDR_W == &gpmc_cfg->cs[cs].nand_cmd) + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND C: %02x\n", cmd & 0xff); + else if (this->IO_ADDR_W == &gpmc_cfg->cs[cs].nand_adr) + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND A: %02x\n", cmd & 0xff); + else if (this->IO_ADDR_W == &gpmc_cfg->cs[cs].nand_dat) + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND D: %02x\n", cmd & 0xff); +#endif writeb(cmd, this->IO_ADDR_W); + } } /* @@ -156,6 +169,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. * @@ -192,6 +224,14 @@ 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 @@ -224,14 +264,308 @@ 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; +} + + +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; + nand_set_features(mtd, 0x90, params); + +#if 1 + nand_get_features(mtd, 0x90, params); + MTDDEBUG(MTD_DEBUG_LEVEL4,"%s: %02x %02x %02x %02x\n", __FUNCTION__, params[0], params[1], params[2], params[3]); +#endif +} + +/** + * nand_read_oob_chipecc - read; get status to seeif chip ECC error + * @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); + + } + } + +} + +static enum omap_nand_ecc_mode current_ecc_method; +enum omap_nand_ecc_mode omap_nand_current_ecc_method(void) +{ + return current_ecc_method; +} + /* * 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; @@ -257,8 +591,50 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.correct = NULL; nand->ecc.calculate = NULL; + /* If currently in BCH then free the priv pointer */ + if (nand->ecc.mode == NAND_ECC_SOFT_BCH && nand->ecc.priv) { + nand_bch_free((struct nand_bch_control *)nand->ecc.priv); + nand->ecc.priv = NULL; + } + /* Setup the ecc configurations again */ - if (hardware) { + if (mode == OMAP_ECC_SOFT_BCH) { + nand->ecc.mode = NAND_ECC_SOFT_BCH; + nand->ecc.calculate = nand_bch_calculate_ecc; + nand->ecc.correct = nand_bch_correct_data; + nand->ecc.read_page = nand_read_page_swecc; + nand->ecc.read_subpage = nand_read_subpage; + nand->ecc.write_page = nand_write_page_swecc; + nand->ecc.read_page_raw = nand_read_page_raw; + nand->ecc.write_page_raw = nand_write_page_raw; + nand->ecc.read_oob = nand_read_oob_std; + nand->ecc.write_oob = nand_write_oob_std; + /* + * Board driver should supply ecc.size and ecc.bytes values to + * select how many bits are correctable; see nand_bch_init() + * for details. + * Otherwise, default to 4 bits for large page devices + */ +#if 1 + /* Since switching, clear out previous ECC setup and let + * BCH figure it out */ + nand->ecc.size = 0; + nand->ecc.bytes = 0; + nand->ecc.layout = NULL; +#endif + if (!nand->ecc.size && (mtd->oobsize >= 64)) { + nand->ecc.size = 512; + nand->ecc.bytes = 7; + } + nand->ecc.priv = nand_bch_init(mtd, + nand->ecc.size, + nand->ecc.bytes, + &nand->ecc.layout); + if (!nand->ecc.priv) { + printk(KERN_WARNING "BCH ECC initialization failed!\n"); + BUG(); + } + } else if (mode == OMAP_ECC_HW) { nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_nand_oob; nand->ecc.size = 512; @@ -267,13 +643,41 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.correct = omap_correct_data; nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); - printf("HW ECC selected\n"); - } else { + printf("NAND: HW ECC selected\n"); + if (nand->has_chip_ecc) + micron_set_chip_ecc(mtd, 0); + } else if (mode == OMAP_ECC_SOFT) { nand->ecc.mode = NAND_ECC_SOFT; /* Use mtd default settings */ nand->ecc.layout = NULL; - printf("SW ECC selected\n"); - } + 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; + } + + current_ecc_method = mode; /* Update NAND handling after ECC mode switch */ nand_scan_tail(mtd); @@ -336,6 +740,11 @@ int board_nand_init(struct nand_chip *nand) if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000) nand->options |= NAND_BUSWIDTH_16; +#ifdef CONFIG_MTD_SKIP_BBTSCAN + /* Skip the bad block scan */ + nand->options |= NAND_SKIP_BBTSCAN; +#endif + nand->chip_delay = 100; /* Default ECC mode */ nand->ecc.mode = NAND_ECC_SOFT; |