summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorJustin Waters <justin.waters@timesys.com>2012-04-17 13:43:17 -0400
committerJustin Waters <justin.waters@timesys.com>2012-04-17 13:43:17 -0400
commit4f60d7e7027af17ceffc1a38e6dbe4e3e95c71ec (patch)
treedd33f3760e08226d5c05036d664d2d68fb3765dc /drivers/mtd
parentb1af6f532e0d348b153d5c148369229d24af361a (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/Makefile1
-rw-r--r--drivers/mtd/mtd_debug.c18
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/nand_base.c239
-rw-r--r--drivers/mtd/nand/nand_bch.c287
-rw-r--r--drivers/mtd/nand/nand_util.c32
-rw-r--r--drivers/mtd/nand/omap_gpmc.c425
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;