summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2011-04-06 09:21:56 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2011-04-06 09:21:56 +0200
commit8dfebf96d79aa8e37c2299af8c02a84d30f5e9e7 (patch)
tree84f4c49a395807cb36dc586b4959eef96b4d6f09 /drivers
parent2a1e60398d74c373285fd118459f20f8255e802b (diff)
parentb672f87cc06ab46e16aca4385bcc4fb68e7a488e (diff)
downloadbarebox-8dfebf96d79aa8e37c2299af8c02a84d30f5e9e7.tar.gz
barebox-8dfebf96d79aa8e37c2299af8c02a84d30f5e9e7.tar.xz
Merge branch 'nand-pu' into next
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/nand/Kconfig41
-rw-r--r--drivers/mtd/nand/Makefile7
-rw-r--r--drivers/mtd/nand/nand.c9
-rw-r--r--drivers/mtd/nand/nand.h30
-rw-r--r--drivers/mtd/nand/nand_base.c1291
-rw-r--r--drivers/mtd/nand/nand_bbt.c9
-rw-r--r--drivers/mtd/nand/nand_hwecc.c101
-rw-r--r--drivers/mtd/nand/nand_hwecc_syndrome.c225
-rw-r--r--drivers/mtd/nand/nand_ids.c145
-rw-r--r--drivers/mtd/nand/nand_swecc.c94
-rw-r--r--drivers/mtd/nand/nand_write.c746
11 files changed, 1398 insertions, 1300 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f335804f67..331aac840a 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -8,6 +8,47 @@ menuconfig NAND
if NAND
+config NAND_WRITE
+ bool
+ default y
+ prompt "Support writing to Nand"
+
+config NAND_ECC_SOFT
+ bool
+ default y
+ prompt "Support software ecc"
+
+config NAND_ECC_HW
+ bool
+ default y
+ prompt "Support hardware ecc"
+
+config NAND_ECC_HW_SYNDROME
+ bool
+ default y
+ prompt "Support syndrome hardware ecc controllers"
+
+config NAND_ECC_HW_NONE
+ bool
+ default y
+ prompt "Support skipping ecc support"
+
+config NAND_INFO
+ bool
+ default y
+ prompt "Nand vendor/size information"
+ help
+ Show informational strings about the vendor and nand flash type
+ during startup
+
+config NAND_BBT
+ bool
+ default y
+ prompt "support bad block tables"
+ help
+ Say y here to include support for bad block tables. This speeds
+ up the process of checking for bad blocks
+
config NAND_IMX
bool
prompt "i.MX NAND driver"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index e9a94b9f5c..fb92f8966b 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -1,8 +1,13 @@
# Generic NAND options
obj-$(CONFIG_NAND) += nand.o nand_ecc.o
+obj-$(CONFIG_NAND_WRITE) += nand_write.o
+obj-$(CONFIG_NAND_ECC_SOFT) += nand_ecc.o nand_swecc.o
+obj-$(CONFIG_NAND_ECC_HW) += nand_hwecc.o
+obj-$(CONFIG_NAND_ECC_HW_SYNDROME) += nand_hwecc_syndrome.o
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
-obj-$(CONFIG_NAND) += nand_base.o nand_bbt.o
+obj-$(CONFIG_NAND) += nand_base.o
+obj-$(CONFIG_NAND_BBT) += nand_bbt.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c
index 08b5cc1512..2333160e29 100644
--- a/drivers/mtd/nand/nand.c
+++ b/drivers/mtd/nand/nand.c
@@ -50,6 +50,7 @@ static ssize_t nand_read(struct cdev *cdev, void* buf, size_t count, ulong offs
#define NOTALIGNED(x) (x & (info->writesize - 1)) != 0
+#ifdef CONFIG_NAND_WRITE
static int all_ff(const void *buf, int len)
{
int i;
@@ -104,6 +105,7 @@ static ssize_t nand_write(struct cdev* cdev, const void *buf, size_t _count, ulo
out:
return ret ? ret : _count;
}
+#endif
static int nand_ioctl(struct cdev *cdev, int request, void *buf)
{
@@ -133,6 +135,7 @@ static int nand_ioctl(struct cdev *cdev, int request, void *buf)
return 0;
}
+#ifdef CONFIG_NAND_WRITE
static ssize_t nand_erase(struct cdev *cdev, size_t count, unsigned long offset)
{
struct mtd_info *info = cdev->priv;
@@ -162,6 +165,8 @@ static ssize_t nand_erase(struct cdev *cdev, size_t count, unsigned long offset)
return 0;
}
+#endif
+
#if 0
static char* mtd_get_size(struct device_d *, struct param_d *param)
{
@@ -171,10 +176,12 @@ static char* mtd_get_size(struct device_d *, struct param_d *param)
static struct file_operations nand_ops = {
.read = nand_read,
+#ifdef CONFIG_NAND_WRITE
.write = nand_write,
+ .erase = nand_erase,
+#endif
.ioctl = nand_ioctl,
.lseek = dev_lseek_default,
- .erase = nand_erase,
};
static ssize_t nand_read_oob(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags)
diff --git a/drivers/mtd/nand/nand.h b/drivers/mtd/nand/nand.h
new file mode 100644
index 0000000000..15bed4e39c
--- /dev/null
+++ b/drivers/mtd/nand/nand.h
@@ -0,0 +1,30 @@
+#ifndef __NAND_H
+#define __NAND_H
+
+int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd);
+int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+ int page);
+int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs);
+int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+ int allowbbt);
+int nand_block_isbad(struct mtd_info *mtd, loff_t offs);
+void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len);
+void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len);
+void single_erase_cmd(struct mtd_info *mtd, int page);
+void multi_erase_cmd(struct mtd_info *mtd, int page);
+void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf);
+int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int page, int cached, int raw);
+int nand_erase(struct mtd_info *mtd, struct erase_info *instr);
+int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const uint8_t *buf);
+int nand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
+
+void nand_init_ecc_hw(struct nand_chip *chip);
+void nand_init_ecc_soft(struct nand_chip *chip);
+void nand_init_ecc_hw_syndrome(struct nand_chip *chip);
+
+#endif /* __NAND_H */
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 22d21277db..f641f8d7ed 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -43,6 +43,8 @@
#include <malloc.h>
#include <module.h>
+#include "nand.h"
+
#ifndef DOXYGEN_SHOULD_SKIP_THIS
/* Define default oob placement schemes for large and small page devices */
@@ -75,12 +77,6 @@ static struct nand_ecclayout nand_oob_64 = {
.length = 38}}
};
-static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
- int new_state);
-
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops);
-
#define DEFINE_LED_TRIGGER(x)
#define DEFINE_LED_TRIGGER_GLOBAL(x)
#define led_trigger_register_simple(x, y) do {} while(0)
@@ -94,24 +90,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
DEFINE_LED_TRIGGER(nand_led_trigger);
/**
- * nand_release_device - [GENERIC] release chip
- * @mtd: MTD device structure
- *
- * Deselect, release chip lock and wake up anyone waiting on the device
- */
-static void nand_release_device(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd->priv;
-
- /* De-select the NAND device */
- chip->select_chip(mtd, -1);
-
- /* Release the controller and the chip */
- chip->controller->active = NULL;
- chip->state = FL_READY;
-}
-
-/**
* nand_read_byte - [DEFAULT] read one byte from the chip
* @mtd: MTD device structure
*
@@ -172,23 +150,6 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
}
/**
- * nand_write_buf - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * Default write function for 8bit buswith
- */
-static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *chip = mtd->priv;
-
- for (i = 0; i < len; i++)
- writeb(buf[i], chip->IO_ADDR_W);
-}
-
-/**
* nand_read_buf - [DEFAULT] read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
@@ -225,26 +186,6 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
}
/**
- * nand_write_buf16 - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- *
- * Default write function for 16bit buswith
- */
-static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
- int i;
- struct nand_chip *chip = mtd->priv;
- u16 *p = (u16 *) buf;
- len >>= 1;
-
- for (i = 0; i < len; i++)
- writew(p[i], chip->IO_ADDR_W);
-
-}
-
-/**
* nand_read_buf16 - [DEFAULT] read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
@@ -304,8 +245,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
if (getchip) {
chipnr = (int)(ofs >> chip->chip_shift);
- nand_get_device(chip, mtd, FL_READING);
-
/* Select the NAND device */
chip->select_chip(mtd, chipnr);
}
@@ -324,70 +263,10 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
res = 1;
}
- if (getchip)
- nand_release_device(mtd);
-
return res;
}
/**
- * nand_default_block_markbad - [DEFAULT] mark a block bad
- * @mtd: MTD device structure
- * @ofs: offset from device start
- *
- * This is the default implementation, which can be overridden by
- * a hardware specific driver.
-*/
-static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
-{
- struct nand_chip *chip = mtd->priv;
- uint8_t buf[2] = { 0, 0 };
- int block, ret;
-
- /* Get block number */
- block = (int)(ofs >> chip->bbt_erase_shift);
- if (chip->bbt)
- chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
-
- /* Do we have a flash based bad block table ? */
- if (chip->options & NAND_USE_FLASH_BBT)
- ret = nand_update_bbt(mtd, ofs);
- else {
- /* We write two bytes, so we dont have to mess with 16 bit
- * access
- */
- nand_get_device(chip, mtd, FL_WRITING);
- ofs += mtd->oobsize;
- chip->ops.len = chip->ops.ooblen = 2;
- chip->ops.datbuf = NULL;
- chip->ops.oobbuf = buf;
- chip->ops.ooboffs = chip->badblockpos & ~0x01;
-
- ret = nand_do_write_oob(mtd, ofs, &chip->ops);
- nand_release_device(mtd);
- }
- if (!ret)
- mtd->ecc_stats.badblocks++;
-
- return ret;
-}
-
-/**
- * nand_check_wp - [GENERIC] check if the chip is write protected
- * @mtd: MTD device structure
- * Check, if the device is write protected
- *
- * The function expects, that the device is already selected
- */
-static int nand_check_wp(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd->priv;
- /* Check the WP bit */
- chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
- return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
-}
-
-/**
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
* @mtd: MTD device structure
* @ofs: offset from device start
@@ -397,16 +276,19 @@ static int nand_check_wp(struct mtd_info *mtd)
* Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function.
*/
-static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
int allowbbt)
{
struct nand_chip *chip = mtd->priv;
+#ifdef CONFIG_NAND_BBT
if (!chip->bbt)
return chip->block_bad(mtd, ofs, getchip);
-
/* Return info from the table */
return nand_isbad_bbt(mtd, ofs, allowbbt);
+#else
+ return chip->block_bad(mtd, ofs, getchip);
+#endif
}
/*
@@ -652,32 +534,6 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
}
/**
- * nand_get_device - [GENERIC] Get chip for selected access
- * @chip: the nand chip descriptor
- * @mtd: MTD device structure
- * @new_state: the state which is requested
- *
- * Get the device and lock it for exclusive access
- */
-static int
-nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
-{
- retry:
- /* Hardware controller shared among independend devices */
- if (!chip->controller->active)
- chip->controller->active = chip;
-
- if (chip->controller->active == chip && chip->state == FL_READY) {
- chip->state = new_state;
- return 0;
- }
- if (new_state == FL_PM_SUSPENDED) {
- return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
- }
- goto retry;
-}
-
-/**
* nand_wait - [DEFAULT] wait until the command is done
* @mtd: MTD device structure
* @chip: NAND chip structure
@@ -739,144 +595,6 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- */
-static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf)
-{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- uint8_t *ecc_code = chip->buffers->ecccode;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
-
- chip->ecc.read_page_raw(mtd, chip, buf);
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
-
- for (i = 0; i < chip->ecc.total; i++)
- ecc_code[i] = chip->oob_poi[eccpos[i]];
-
- eccsteps = chip->ecc.steps;
- p = buf;
-
- for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
-
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
- if (stat < 0)
- mtd->ecc_stats.failed++;
- else
- mtd->ecc_stats.corrected += stat;
- }
- return 0;
-}
-
-/**
- * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- *
- * Not for syndrome calculating ecc controllers which need a special oob layout
- */
-static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf)
-{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- uint8_t *ecc_code = chip->buffers->ecccode;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
- }
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
- for (i = 0; i < chip->ecc.total; i++)
- ecc_code[i] = chip->oob_poi[eccpos[i]];
-
- eccsteps = chip->ecc.steps;
- p = buf;
-
- for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
-
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
- if (stat < 0)
- mtd->ecc_stats.failed++;
- else
- mtd->ecc_stats.corrected += stat;
- }
- return 0;
-}
-
-/**
- * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- *
- * The hw generator calculates the error syndrome automatically. Therefor
- * we need a special oob layout and handling.
- */
-static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf)
-{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *p = buf;
- uint8_t *oob = chip->oob_poi;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
-
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
-
- if (chip->ecc.prepad) {
- chip->read_buf(mtd, oob, chip->ecc.prepad);
- oob += chip->ecc.prepad;
- }
-
- chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
- chip->read_buf(mtd, oob, eccbytes);
- stat = chip->ecc.correct(mtd, p, oob, NULL);
-
- if (stat < 0)
- mtd->ecc_stats.failed++;
- else
- mtd->ecc_stats.corrected += stat;
-
- oob += eccbytes;
-
- if (chip->ecc.postpad) {
- chip->read_buf(mtd, oob, chip->ecc.postpad);
- oob += chip->ecc.postpad;
- }
- }
-
- /* Calculate remaining oob bytes */
- i = mtd->oobsize - (oob - chip->oob_poi);
- if (i)
- chip->read_buf(mtd, oob, i);
-
- return 0;
-}
-
-/**
* nand_transfer_oob - [Internal] Transfer oob to client buffer
* @chip: nand chip structure
* @oob: oob destination address
@@ -1081,8 +799,6 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
if (!len)
return 0;
- nand_get_device(chip, mtd, FL_READING);
-
chip->ops.len = len;
chip->ops.datbuf = buf;
chip->ops.oobbuf = NULL;
@@ -1091,8 +807,6 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
*retlen = chip->ops.retlen;
- nand_release_device(mtd);
-
return ret;
}
@@ -1103,7 +817,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,
+int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int page, int sndcmd)
{
if (sndcmd) {
@@ -1115,127 +829,6 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC
- * with syndromes
- * @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 nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
- int page, int sndcmd)
-{
- uint8_t *buf = chip->oob_poi;
- int length = mtd->oobsize;
- int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
- int eccsize = chip->ecc.size;
- uint8_t *bufpoi = buf;
- int i, toread, sndrnd = 0, pos;
-
- chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
- for (i = 0; i < chip->ecc.steps; i++) {
- if (sndrnd) {
- pos = eccsize + i * (eccsize + chunk);
- if (mtd->writesize > 512)
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1);
- else
- chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page);
- } else
- sndrnd = 1;
- toread = min_t(int, length, chunk);
- chip->read_buf(mtd, bufpoi, toread);
- bufpoi += toread;
- length -= toread;
- }
- if (length > 0)
- chip->read_buf(mtd, bufpoi, length);
-
- return 1;
-}
-
-/**
- * nand_write_oob_std - [REPLACABLE] the most common OOB data write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
- */
-static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
-{
- int status = 0;
- const uint8_t *buf = chip->oob_poi;
- int length = mtd->oobsize;
-
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
- chip->write_buf(mtd, buf, length);
- /* Send command to program the OOB data */
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-
- status = chip->waitfunc(mtd, chip);
-
- return status & NAND_STATUS_FAIL ? -EIO : 0;
-}
-
-/**
- * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC
- * with syndrome - only for large page flash !
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
- */
-static int nand_write_oob_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
-{
- int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
- int eccsize = chip->ecc.size, length = mtd->oobsize;
- int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
- const uint8_t *bufpoi = chip->oob_poi;
-
- /*
- * data-ecc-data-ecc ... ecc-oob
- * or
- * data-pad-ecc-pad-data-pad .... ecc-pad-oob
- */
- if (!chip->ecc.prepad && !chip->ecc.postpad) {
- pos = steps * (eccsize + chunk);
- steps = 0;
- } else
- pos = eccsize;
-
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
- for (i = 0; i < steps; i++) {
- if (sndcmd) {
- if (mtd->writesize <= 512) {
- uint32_t fill = 0xFFFFFFFF;
-
- len = eccsize;
- while (len > 0) {
- int num = min_t(int, len, 4);
- chip->write_buf(mtd, (uint8_t *)&fill,
- num);
- len -= num;
- }
- } else {
- pos = eccsize + i * (eccsize + chunk);
- chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1);
- }
- } else
- sndcmd = 1;
- len = min_t(int, length, chunk);
- chip->write_buf(mtd, bufpoi, len);
- bufpoi += len;
- length -= len;
- }
- if (length > 0)
- chip->write_buf(mtd, bufpoi, length);
-
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
-
- return status & NAND_STATUS_FAIL ? -EIO : 0;
-}
-
-/**
* nand_do_read_oob - [Intern] NAND read out-of-band
* @mtd: MTD device structure
* @from: offset to read from
@@ -1339,7 +932,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- struct nand_chip *chip = mtd->priv;
int ret = -ENOSYS;
ops->retlen = 0;
@@ -1351,8 +943,6 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
return -EINVAL;
}
- nand_get_device(chip, mtd, FL_READING);
-
switch(ops->mode) {
case MTD_OOB_PLACE:
case MTD_OOB_AUTO:
@@ -1369,742 +959,15 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
ret = nand_do_read_ops(mtd, from, ops);
out:
- nand_release_device(mtd);
return ret;
}
-
-/**
- * nand_write_page_raw - [Intern] raw page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- */
-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);
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-}
-
-/**
- * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- */
-static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf)
-{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- const uint8_t *p = buf;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
-
- /* Software ecc calculation */
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
-
- for (i = 0; i < chip->ecc.total; i++)
- chip->oob_poi[eccpos[i]] = ecc_calc[i];
-
- chip->ecc.write_page_raw(mtd, chip, buf);
-}
-
-/**
- * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- */
-static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf)
-{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- const uint8_t *p = buf;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
- chip->write_buf(mtd, p, eccsize);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
- }
-
- for (i = 0; i < chip->ecc.total; i++)
- chip->oob_poi[eccpos[i]] = ecc_calc[i];
-
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-}
-
-/**
- * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
- *
- * The hw generator calculates the error syndrome automatically. Therefor
- * we need a special oob layout and handling.
- */
-static void nand_write_page_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf)
-{
- int i, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- const uint8_t *p = buf;
- uint8_t *oob = chip->oob_poi;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
-
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
- chip->write_buf(mtd, p, eccsize);
-
- if (chip->ecc.prepad) {
- chip->write_buf(mtd, oob, chip->ecc.prepad);
- oob += chip->ecc.prepad;
- }
-
- chip->ecc.calculate(mtd, p, oob);
- chip->write_buf(mtd, oob, eccbytes);
- oob += eccbytes;
-
- if (chip->ecc.postpad) {
- chip->write_buf(mtd, oob, chip->ecc.postpad);
- oob += chip->ecc.postpad;
- }
- }
-
- /* Calculate remaining oob bytes */
- i = mtd->oobsize - (oob - chip->oob_poi);
- if (i)
- chip->write_buf(mtd, oob, i);
-}
-
-/**
- * nand_write_page - [REPLACEABLE] write one page
- * @mtd: MTD device structure
- * @chip: NAND chip descriptor
- * @buf: the data to write
- * @page: page number to write
- * @cached: cached programming
- * @raw: use _raw version of write_page
- */
-static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int page, int cached, int raw)
-{
- int status;
-
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-
- if (unlikely(raw))
- chip->ecc.write_page_raw(mtd, chip, buf);
- else
- chip->ecc.write_page(mtd, chip, buf);
-
- /*
- * Cached progamming disabled for now, Not sure if its worth the
- * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
- */
- cached = 0;
-
- if (!cached || !(chip->options & NAND_CACHEPRG)) {
-
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
- /*
- * See if operation failed and additional status checks are
- * available
- */
- if ((status & NAND_STATUS_FAIL) && (chip->errstat))
- status = chip->errstat(mtd, chip, FL_WRITING, status,
- page);
-
- if (status & NAND_STATUS_FAIL) {
- return -EIO;
- }
- } else {
- chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
- }
-
-#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
- /* Send command to read back the data */
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
-
- if (chip->verify_buf(mtd, buf, mtd->writesize))
- return -EIO;
-#endif
- return 0;
-}
-
-/**
- * nand_fill_oob - [Internal] Transfer client buffer to oob
- * @chip: nand chip structure
- * @oob: oob data buffer
- * @ops: oob ops structure
- */
-static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
- struct mtd_oob_ops *ops)
-{
- size_t len = ops->ooblen;
-
- switch(ops->mode) {
-
- case MTD_OOB_PLACE:
- case MTD_OOB_RAW:
- memcpy(chip->oob_poi + ops->ooboffs, oob, len);
- return oob + len;
-
- case MTD_OOB_AUTO: {
- struct nand_oobfree *free = chip->ecc.layout->oobfree;
- uint32_t boffs = 0, woffs = ops->ooboffs;
- size_t bytes = 0;
-
- for(; free->length && len; free++, len -= bytes) {
- /* Write request not from offset 0 ? */
- if (unlikely(woffs)) {
- if (woffs >= free->length) {
- woffs -= free->length;
- continue;
- }
- boffs = free->offset + woffs;
- bytes = min_t(size_t, len,
- (free->length - woffs));
- woffs = 0;
- } else {
- bytes = min_t(size_t, len, free->length);
- boffs = free->offset;
- }
- memcpy(chip->oob_poi + boffs, oob, bytes);
- oob += bytes;
- }
- return oob;
- }
- default:
- BUG();
- }
- return NULL;
-}
-
-#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
-
-/**
- * nand_do_write_ops - [Internal] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operations description structure
- *
- * NAND write with ECC
- */
-static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- int chipnr, realpage, page, blockmask, column;
- struct nand_chip *chip = mtd->priv;
- uint32_t writelen = ops->len;
- uint8_t *oob = ops->oobbuf;
- uint8_t *buf = ops->datbuf;
- int ret, subpage;
-
- ops->retlen = 0;
- if (!writelen)
- return 0;
-
- /* reject writes, which are not page aligned */
- if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
- printk(KERN_NOTICE "nand_write: "
- "Attempt to write not page aligned data\n");
- return -EINVAL;
- }
-
- column = to & (mtd->writesize - 1);
- subpage = column || (writelen & (mtd->writesize - 1));
-
- if (subpage && oob)
- return -EINVAL;
-
- chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- return -EIO;
- }
-
- realpage = (int)(to >> chip->page_shift);
- page = realpage & chip->pagemask;
- blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
-
- /* Invalidate the page cache, when we write to the cached page */
- if (to <= (chip->pagebuf << chip->page_shift) &&
- (chip->pagebuf << chip->page_shift) < (to + ops->len))
- chip->pagebuf = -1;
-
- /* If we're not given explicit OOB data, let it be 0xFF */
- if (likely(!oob))
- memset(chip->oob_poi, 0xff, mtd->oobsize);
-
- while(1) {
- int bytes = mtd->writesize;
- int cached = writelen > bytes && page != blockmask;
- uint8_t *wbuf = buf;
-
- /* Partial page write ? */
- if (unlikely(column || writelen < (mtd->writesize - 1))) {
- cached = 0;
- bytes = min_t(int, bytes - column, (int) writelen);
- chip->pagebuf = -1;
- memset(chip->buffers->databuf, 0xff, mtd->writesize);
- memcpy(&chip->buffers->databuf[column], buf, bytes);
- wbuf = chip->buffers->databuf;
- }
-
- if (unlikely(oob))
- oob = nand_fill_oob(chip, oob, ops);
-
- ret = chip->write_page(mtd, chip, wbuf, page, cached,
- (ops->mode == MTD_OOB_RAW));
- if (ret)
- break;
-
- writelen -= bytes;
- if (!writelen)
- break;
-
- column = 0;
- buf += bytes;
- realpage++;
-
- page = realpage & chip->pagemask;
- /* Check, if we cross a chip boundary */
- if (!page) {
- chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
- }
- }
-
- ops->retlen = ops->len - writelen;
- if (unlikely(oob))
- ops->oobretlen = ops->ooblen;
- return ret;
-}
-
-/**
- * nand_write - [MTD Interface] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @len: number of bytes to write
- * @retlen: pointer to variable to store the number of written bytes
- * @buf: the data to write
- *
- * NAND write with ECC
- */
-static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const uint8_t *buf)
-{
- struct nand_chip *chip = mtd->priv;
- int ret;
-
- /* Do not allow reads past end of device */
- if ((to + len) > mtd->size)
- return -EINVAL;
- if (!len)
- return 0;
-
- nand_get_device(chip, mtd, FL_WRITING);
-
- chip->ops.len = len;
- chip->ops.datbuf = (uint8_t *)buf;
- chip->ops.oobbuf = NULL;
-
- ret = nand_do_write_ops(mtd, to, &chip->ops);
-
- *retlen = chip->ops.retlen;
-
- nand_release_device(mtd);
-
- return ret;
-}
-
-/**
- * nand_do_write_oob - [MTD Interface] NAND write out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
- *
- * NAND write out-of-band
- */
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- int chipnr, page, status, len;
- struct nand_chip *chip = mtd->priv;
-
- MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
- (unsigned int)to, (int)ops->ooblen);
-
- if (ops->mode == MTD_OOB_AUTO)
- len = chip->ecc.layout->oobavail;
- else
- len = mtd->oobsize;
-
- /* Do not allow write past end of page */
- if ((ops->ooboffs + ops->ooblen) > len) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
- "Attempt to write past end of page\n");
- return -EINVAL;
- }
-
- if (unlikely(ops->ooboffs >= len)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
- "Attempt to start write outside oob\n");
- return -EINVAL;
- }
-
- /* Do not allow reads past end of device */
- if (unlikely(to >= mtd->size ||
- ops->ooboffs + ops->ooblen >
- ((mtd->size >> chip->page_shift) -
- (to >> chip->page_shift)) * len)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
- "Attempt write beyond end of device\n");
- return -EINVAL;
- }
-
- chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
- /* Shift to get page */
- page = (int)(to >> chip->page_shift);
-
- /*
- * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
- * of my DiskOnChip 2000 test units) will clear the whole data page too
- * if we don't do this. I have no clue why, but I seem to have 'fixed'
- * it in the doc2000 driver in August 1999. dwmw2.
- */
- chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd))
- return -EROFS;
-
- /* Invalidate the page cache, if we write to the cached page */
- if (page == chip->pagebuf)
- chip->pagebuf = -1;
-
- memset(chip->oob_poi, 0xff, mtd->oobsize);
- nand_fill_oob(chip, ops->oobbuf, ops);
- status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
- memset(chip->oob_poi, 0xff, mtd->oobsize);
-
- if (status)
- return status;
-
- ops->oobretlen = ops->ooblen;
-
- return 0;
-}
-
-/**
- * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
- */
-static int nand_write_oob(struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops)
-{
- struct nand_chip *chip = mtd->priv;
- int ret = -ENOSYS;
-
- ops->retlen = 0;
-
- /* Do not allow writes past end of device */
- if (ops->datbuf && (to + ops->len) > mtd->size) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
- "Attempt read beyond end of device\n");
- return -EINVAL;
- }
-
- nand_get_device(chip, mtd, FL_WRITING);
-
- switch(ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
- case MTD_OOB_RAW:
- break;
-
- default:
- goto out;
- }
-
- if (!ops->datbuf)
- ret = nand_do_write_oob(mtd, to, ops);
- else
- ret = nand_do_write_ops(mtd, to, ops);
-
- out:
- nand_release_device(mtd);
- return ret;
-}
-
-/**
- * single_erease_cmd - [GENERIC] NAND standard block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
- *
- * Standard erase command for NAND chips
- */
-static void single_erase_cmd(struct mtd_info *mtd, int page)
-{
- struct nand_chip *chip = mtd->priv;
- /* Send commands to erase a block */
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
- chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
-}
-
-/**
- * multi_erease_cmd - [GENERIC] AND specific block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
- *
- * AND multi block erase command function
- * Erase 4 consecutive blocks
- */
-static void multi_erase_cmd(struct mtd_info *mtd, int page)
-{
- struct nand_chip *chip = mtd->priv;
- /* Send commands to erase a block */
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
- chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
- chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
-}
-
-/**
- * nand_erase - [MTD Interface] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- *
- * Erase one ore more blocks
- */
-static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
- return nand_erase_nand(mtd, instr, 0);
-}
-
-#define BBT_PAGE_MASK 0xffffff3f
-/**
- * nand_erase_nand - [Internal] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- * @allowbbt: allow erasing the bbt area
- *
- * Erase one ore more blocks
- */
-int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
- int allowbbt)
-{
- int page, len, status, pages_per_block, ret, chipnr;
- struct nand_chip *chip = mtd->priv;
- int rewrite_bbt[NAND_MAX_CHIPS]={0};
- unsigned int bbt_masked_page = 0xffffffff;
-
- MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n",
- (unsigned int)instr->addr, (unsigned int)instr->len);
-
- /* Start address must align on block boundary */
- if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
- return -EINVAL;
- }
-
- /* Length must align on block boundary */
- if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
- "Length not block aligned\n");
- return -EINVAL;
- }
-
- /* Do not allow erase past end of device */
- if ((instr->len + instr->addr) > mtd->size) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
- "Erase past end of device\n");
- return -EINVAL;
- }
-
- instr->fail_addr = 0xffffffff;
-
- /* Grab the lock and see if the device is available */
- nand_get_device(chip, mtd, FL_ERASING);
-
- /* Shift to get first page */
- page = (int)(instr->addr >> chip->page_shift);
- chipnr = (int)(instr->addr >> chip->chip_shift);
-
- /* Calculate pages in each block */
- pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
-
- /* Select the NAND device */
- chip->select_chip(mtd, chipnr);
-
- /* Check, if it is write protected */
- if (nand_check_wp(mtd)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
- "Device is write protected!!!\n");
- instr->state = MTD_ERASE_FAILED;
- goto erase_exit;
- }
-
- /*
- * If BBT requires refresh, set the BBT page mask to see if the BBT
- * should be rewritten. Otherwise the mask is set to 0xffffffff which
- * can not be matched. This is also done when the bbt is actually
- * erased to avoid recusrsive updates
- */
- if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
- bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
-
- /* Loop through the pages */
- len = instr->len;
-
- instr->state = MTD_ERASING;
-
- while (len) {
- /*
- * heck if we have a bad block, we do not erase bad blocks !
- */
- if (nand_block_checkbad(mtd, ((loff_t) page) <<
- chip->page_shift, 0, allowbbt)) {
- printk(KERN_WARNING "nand_erase: attempt to erase a "
- "bad block at page 0x%08x\n", page);
- instr->state = MTD_ERASE_FAILED;
- goto erase_exit;
- }
-
- /*
- * Invalidate the page cache, if we erase the block which
- * contains the current cached page
- */
- if (page <= chip->pagebuf && chip->pagebuf <
- (page + pages_per_block))
- chip->pagebuf = -1;
-
- chip->erase_cmd(mtd, page & chip->pagemask);
-
- status = chip->waitfunc(mtd, chip);
-
- /*
- * See if operation failed and additional status checks are
- * available
- */
- if ((status & NAND_STATUS_FAIL) && (chip->errstat))
- status = chip->errstat(mtd, chip, FL_ERASING,
- status, page);
-
- /* See if block erase succeeded */
- if (status & NAND_STATUS_FAIL) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
- "Failed erase, page 0x%08x\n", page);
- instr->state = MTD_ERASE_FAILED;
- instr->fail_addr = (page << chip->page_shift);
- goto erase_exit;
- }
-
- /*
- * If BBT requires refresh, set the BBT rewrite flag to the
- * page being erased
- */
- if (bbt_masked_page != 0xffffffff &&
- (page & BBT_PAGE_MASK) == bbt_masked_page)
- rewrite_bbt[chipnr] = (page << chip->page_shift);
-
- /* Increment page address and decrement length */
- len -= (1 << chip->phys_erase_shift);
- page += pages_per_block;
-
- /* Check, if we cross a chip boundary */
- if (len && !(page & chip->pagemask)) {
- chipnr++;
- chip->select_chip(mtd, -1);
- chip->select_chip(mtd, chipnr);
-
- /*
- * If BBT requires refresh and BBT-PERCHIP, set the BBT
- * page mask to see if this BBT should be rewritten
- */
- if (bbt_masked_page != 0xffffffff &&
- (chip->bbt_td->options & NAND_BBT_PERCHIP))
- bbt_masked_page = chip->bbt_td->pages[chipnr] &
- BBT_PAGE_MASK;
- }
- }
- instr->state = MTD_ERASE_DONE;
-
- erase_exit:
-
- ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
-
- /* Deselect and wake up anyone waiting on the device */
- nand_release_device(mtd);
-
- /* Do call back function */
- if (!ret)
- mtd_erase_callback(instr);
-
- /*
- * If BBT requires refresh and erase was successful, rewrite any
- * selected bad block tables
- */
- if (bbt_masked_page == 0xffffffff || ret)
- return ret;
-
- for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
- if (!rewrite_bbt[chipnr])
- continue;
- /* update the BBT for chip */
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "
- "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
- chip->bbt_td->pages[chipnr]);
- nand_update_bbt(mtd, rewrite_bbt[chipnr]);
- }
-
- /* Return more or less happy */
- return ret;
-}
-
-/**
- * nand_sync - [MTD Interface] sync
- * @mtd: MTD device structure
- *
- * Sync is actually a wait for chip ready function
- */
-static void nand_sync(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd->priv;
-
- MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n");
-
- /* Grab the lock and see if the device is available */
- nand_get_device(chip, mtd, FL_SYNCING);
- /* Release it and go back */
- nand_release_device(mtd);
-}
-
/**
* nand_block_isbad - [MTD Interface] Check if block at offset is bad
* @mtd: MTD device structure
* @offs: offset relative to mtd start
*/
-static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
+int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
{
/* Check for invalid offset */
if (offs > mtd->size)
@@ -2133,32 +996,6 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
return chip->block_markbad(mtd, ofs);
}
-/**
- * nand_suspend - [MTD Interface] Suspend the NAND flash
- * @mtd: MTD device structure
- */
-static int nand_suspend(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd->priv;
-
- return nand_get_device(chip, mtd, FL_PM_SUSPENDED);
-}
-
-/**
- * nand_resume - [MTD Interface] Resume the NAND flash
- * @mtd: MTD device structure
- */
-static void nand_resume(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd->priv;
-
- if (chip->state == FL_PM_SUSPENDED)
- nand_release_device(mtd);
- else
- printk(KERN_ERR "nand_resume() called for a chip which is not "
- "in suspended state\n");
-}
-
/*
* Set default functions
*/
@@ -2184,17 +1021,20 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
chip->read_word = nand_read_word;
if (!chip->block_bad)
chip->block_bad = nand_block_bad;
+#ifdef CONFIG_NAND_WRITE
if (!chip->block_markbad)
chip->block_markbad = nand_default_block_markbad;
if (!chip->write_buf)
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+#endif
if (!chip->read_buf)
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!chip->verify_buf)
chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
+#ifdef CONFIG_NAND_BBT
if (!chip->scan_bbt)
chip->scan_bbt = nand_default_bbt;
-
+#endif
if (!chip->controller) {
chip->controller = &chip->hwcontrol;
}
@@ -2341,12 +1181,13 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+#ifdef CONFIG_NAND_WRITE
/* Check for AND chips with 4 page planes */
if (chip->options & NAND_4PAGE_ARRAY)
chip->erase_cmd = multi_erase_cmd;
else
chip->erase_cmd = single_erase_cmd;
-
+#endif
/* Do not replace user supplied command function ! */
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;
@@ -2408,6 +1249,23 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
return 0;
}
+static void __maybe_unused nand_check_hwecc(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ if ((!chip->ecc.calculate || !chip->ecc.correct ||
+ !chip->ecc.hwctl) &&
+ (!chip->ecc.read_page || !chip->ecc.write_page)) {
+ printk(KERN_WARNING "No ECC functions supplied, "
+ "Hardware ECC not possible\n");
+ BUG();
+ }
+
+ if (mtd->writesize < chip->ecc.size) {
+ printk(KERN_WARNING "%d byte HW ECC not possible on "
+ "%d byte page size\n",
+ chip->ecc.size, mtd->writesize);
+ BUG();
+ }
+}
/**
* nand_scan_tail - [NAND Interface] Scan for the NAND device
@@ -2452,8 +1310,10 @@ int nand_scan_tail(struct mtd_info *mtd)
}
}
+#ifdef CONFIG_NAND_WRITE
if (!chip->write_page)
chip->write_page = nand_write_page;
+#endif
/*
* check ECC mode, default to software if 3byte/512byte hardware ECC is
@@ -2461,71 +1321,42 @@ int nand_scan_tail(struct mtd_info *mtd)
*/
if (!chip->ecc.read_page_raw)
chip->ecc.read_page_raw = nand_read_page_raw;
+#ifdef CONFIG_NAND_WRITE
if (!chip->ecc.write_page_raw)
chip->ecc.write_page_raw = nand_write_page_raw;
-
+#endif
switch (chip->ecc.mode) {
+#ifdef CONFIG_NAND_ECC_HW
case NAND_ECC_HW:
- /* Use standard hwecc read page function ? */
- if (!chip->ecc.read_page)
- chip->ecc.read_page = nand_read_page_hwecc;
- if (!chip->ecc.write_page)
- chip->ecc.write_page = nand_write_page_hwecc;
- if (!chip->ecc.read_oob)
- chip->ecc.read_oob = nand_read_oob_std;
- if (!chip->ecc.write_oob)
- chip->ecc.write_oob = nand_write_oob_std;
-
+ nand_check_hwecc(mtd, chip);
+ nand_init_ecc_hw(chip);
+ break;
+#endif
+#ifdef CONFIG_NAND_ECC_HW_SYNDROME
case NAND_ECC_HW_SYNDROME:
- if ((!chip->ecc.calculate || !chip->ecc.correct ||
- !chip->ecc.hwctl) &&
- (!chip->ecc.read_page ||
- chip->ecc.read_page == nand_read_page_hwecc ||
- !chip->ecc.write_page ||
- chip->ecc.write_page == nand_write_page_hwecc)) {
- printk(KERN_WARNING "No ECC functions supplied, "
- "Hardware ECC not possible\n");
- BUG();
- }
- /* Use standard syndrome read/write page function ? */
- if (!chip->ecc.read_page)
- chip->ecc.read_page = nand_read_page_syndrome;
- if (!chip->ecc.write_page)
- chip->ecc.write_page = nand_write_page_syndrome;
- if (!chip->ecc.read_oob)
- chip->ecc.read_oob = nand_read_oob_syndrome;
- if (!chip->ecc.write_oob)
- chip->ecc.write_oob = nand_write_oob_syndrome;
-
- if (mtd->writesize >= chip->ecc.size)
- break;
- printk(KERN_WARNING "%d byte HW ECC not possible on "
- "%d byte page size, fallback to SW ECC\n",
- chip->ecc.size, mtd->writesize);
- chip->ecc.mode = NAND_ECC_SOFT;
-
+ nand_check_hwecc(mtd, chip);
+ nand_init_ecc_hw_syndrome(chip);
+ break;
+#endif
+#ifdef CONFIG_NAND_ECC_SOFT
case NAND_ECC_SOFT:
- chip->ecc.calculate = nand_calculate_ecc;
- chip->ecc.correct = nand_correct_data;
- chip->ecc.read_page = nand_read_page_swecc;
- chip->ecc.write_page = nand_write_page_swecc;
- chip->ecc.read_oob = nand_read_oob_std;
- chip->ecc.write_oob = nand_write_oob_std;
- chip->ecc.size = 256;
- chip->ecc.bytes = 3;
+ nand_init_ecc_soft(chip);
break;
-
+#endif
+#ifdef CONFIG_NAND_ECC_NONE
case NAND_ECC_NONE:
printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
"This is not recommended !!\n");
chip->ecc.read_page = nand_read_page_raw;
+#ifdef CONFIG_NAND_WRITE
chip->ecc.write_page = nand_write_page_raw;
- chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.write_oob = nand_write_oob_std;
+#endif
+ chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.size = mtd->writesize;
chip->ecc.bytes = 0;
break;
-
+#endif
default:
printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
chip->ecc.mode);
@@ -2583,16 +1414,15 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
+#ifdef CONFIG_NAND_WRITE
mtd->erase = nand_erase;
- mtd->read = nand_read;
mtd->write = nand_write;
- mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
- mtd->sync = nand_sync;
+#endif
+ mtd->read = nand_read;
+ mtd->read_oob = nand_read_oob;
mtd->lock = NULL;
mtd->unlock = NULL;
- mtd->suspend = nand_suspend;
- mtd->resume = nand_resume;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
@@ -2602,9 +1432,12 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
-
+#ifdef CONFIG_NAND_BBT
/* Build bad block table */
return chip->scan_bbt(mtd);
+#else
+ return 0;
+#endif
}
/**
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 4a6bf390a4..bf3a7dbc74 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -557,6 +557,7 @@ static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt
* (Re)write the bad block table
*
*/
+#ifdef CONFIG_NAND_WRITE
static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md,
int chipsel)
@@ -745,6 +746,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
"nand_bbt: Error while writing bad block table %d\n", res);
return res;
}
+#else
+static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md,
+ int chipsel)
+{
+ return 0;
+}
+#endif
/**
* nand_memory_bbt - [GENERIC] create a memory based bad block table
diff --git a/drivers/mtd/nand/nand_hwecc.c b/drivers/mtd/nand/nand_hwecc.c
new file mode 100644
index 0000000000..e5de30a99f
--- /dev/null
+++ b/drivers/mtd/nand/nand_hwecc.c
@@ -0,0 +1,101 @@
+#include <common.h>
+#include <errno.h>
+#include <clock.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/err.h>
+#include <linux/mtd/nand_ecc.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <malloc.h>
+
+#include "nand.h"
+
+/**
+ * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ *
+ * Not for syndrome calculating ecc controllers which need a special oob layout
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ }
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+}
+
+/**
+ * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ */
+#ifdef CONFIG_NAND_WRITE
+static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ const uint8_t *p = buf;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+ chip->write_buf(mtd, p, eccsize);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ }
+
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
+#endif
+
+void nand_init_ecc_hw(struct nand_chip *chip)
+{
+ /* Use standard hwecc read page function ? */
+ if (!chip->ecc.read_page)
+ chip->ecc.read_page = nand_read_page_hwecc;
+ if (!chip->ecc.read_oob)
+ chip->ecc.read_oob = nand_read_oob_std;
+#ifdef CONFIG_NAND_WRITE
+ if (!chip->ecc.write_oob)
+ chip->ecc.write_oob = nand_write_oob_std;
+ if (!chip->ecc.write_page)
+ chip->ecc.write_page = nand_write_page_hwecc;
+#endif
+}
diff --git a/drivers/mtd/nand/nand_hwecc_syndrome.c b/drivers/mtd/nand/nand_hwecc_syndrome.c
new file mode 100644
index 0000000000..9a66180952
--- /dev/null
+++ b/drivers/mtd/nand/nand_hwecc_syndrome.c
@@ -0,0 +1,225 @@
+#include <common.h>
+#include <errno.h>
+#include <clock.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/err.h>
+#include <linux/mtd/nand_ecc.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <module.h>
+
+/**
+ * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ *
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and handling.
+ */
+static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+
+ if (chip->ecc.prepad) {
+ chip->read_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
+ chip->read_buf(mtd, oob, eccbytes);
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->read_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i)
+ chip->read_buf(mtd, oob, i);
+
+ return 0;
+}
+/**
+ * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ *
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and handling.
+ */
+#ifdef CONFIG_NAND_WRITE
+static void nand_write_page_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ const uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+ chip->write_buf(mtd, p, eccsize);
+
+ if (chip->ecc.prepad) {
+ chip->write_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ chip->ecc.calculate(mtd, p, oob);
+ chip->write_buf(mtd, oob, eccbytes);
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->write_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i)
+ chip->write_buf(mtd, oob, i);
+}
+#endif
+
+/**
+ * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC
+ * with syndromes
+ * @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 nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd)
+{
+ uint8_t *buf = chip->oob_poi;
+ int length = mtd->oobsize;
+ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsize = chip->ecc.size;
+ uint8_t *bufpoi = buf;
+ int i, toread, sndrnd = 0, pos;
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
+ for (i = 0; i < chip->ecc.steps; i++) {
+ if (sndrnd) {
+ pos = eccsize + i * (eccsize + chunk);
+ if (mtd->writesize > 512)
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1);
+ else
+ chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page);
+ } else
+ sndrnd = 1;
+ toread = min_t(int, length, chunk);
+ chip->read_buf(mtd, bufpoi, toread);
+ bufpoi += toread;
+ length -= toread;
+ }
+ if (length > 0)
+ chip->read_buf(mtd, bufpoi, length);
+
+ return 1;
+}
+
+/**
+ * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC
+ * with syndrome - only for large page flash !
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+#ifdef CONFIG_NAND_WRITE
+static int nand_write_oob_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsize = chip->ecc.size, length = mtd->oobsize;
+ int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
+ const uint8_t *bufpoi = chip->oob_poi;
+
+ /*
+ * data-ecc-data-ecc ... ecc-oob
+ * or
+ * data-pad-ecc-pad-data-pad .... ecc-pad-oob
+ */
+ if (!chip->ecc.prepad && !chip->ecc.postpad) {
+ pos = steps * (eccsize + chunk);
+ steps = 0;
+ } else
+ pos = eccsize;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
+ for (i = 0; i < steps; i++) {
+ if (sndcmd) {
+ if (mtd->writesize <= 512) {
+ uint32_t fill = 0xFFFFFFFF;
+
+ len = eccsize;
+ while (len > 0) {
+ int num = min_t(int, len, 4);
+ chip->write_buf(mtd, (uint8_t *)&fill,
+ num);
+ len -= num;
+ }
+ } else {
+ pos = eccsize + i * (eccsize + chunk);
+ chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1);
+ }
+ } else
+ sndcmd = 1;
+ len = min_t(int, length, chunk);
+ chip->write_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ length -= len;
+ }
+ if (length > 0)
+ chip->write_buf(mtd, bufpoi, length);
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+#endif
+
+void nand_init_ecc_hw_syndrome(struct nand_chip *chip)
+{
+ /* Use standard syndrome read/write page function ? */
+ if (!chip->ecc.read_page)
+ chip->ecc.read_page = nand_read_page_syndrome;
+ if (!chip->ecc.read_oob)
+ chip->ecc.read_oob = nand_read_oob_syndrome;
+#ifdef CONFIG_NAND_WRITE
+ if (!chip->ecc.write_page)
+ chip->ecc.write_page = nand_write_page_syndrome;
+ if (!chip->ecc.write_oob)
+ chip->ecc.write_oob = nand_write_oob_syndrome;
+#endif
+}
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index f95975c9ea..cb53fc61ca 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -12,6 +12,13 @@
*/
#include <common.h>
#include <linux/mtd/nand.h>
+
+#ifdef CONFIG_NAND_INFO
+#define __NANDSTR(str) str
+#else
+#define __NANDSTR(str) ""
+#endif
+
/*
* Chip ID list
*
@@ -26,47 +33,47 @@
struct nand_flash_dev nand_flash_ids[] = {
#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
- {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
- {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
- {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
- {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},
- {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},
- {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},
- {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},
- {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},
- {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},
- {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},
-
- {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},
- {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
- {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
- {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 1MiB 5V 8-bit"), 0x6e, 256, 1, 0x1000, 0},
+ {__NANDSTR("NAND 2MiB 5V 8-bit"), 0x64, 256, 2, 0x1000, 0},
+ {__NANDSTR("NAND 4MiB 5V 8-bit"), 0x6b, 512, 4, 0x2000, 0},
+ {__NANDSTR("NAND 1MiB 3,3V 8-bit"), 0xe8, 256, 1, 0x1000, 0},
+ {__NANDSTR("NAND 1MiB 3,3V 8-bit"), 0xec, 256, 1, 0x1000, 0},
+ {__NANDSTR("NAND 2MiB 3,3V 8-bit"), 0xea, 256, 2, 0x1000, 0},
+ {__NANDSTR("NAND 4MiB 3,3V 8-bit"), 0xd5, 512, 4, 0x2000, 0},
+ {__NANDSTR("NAND 4MiB 3,3V 8-bit"), 0xe3, 512, 4, 0x2000, 0},
+ {__NANDSTR("NAND 4MiB 3,3V 8-bit"), 0xe5, 512, 4, 0x2000, 0},
+ {__NANDSTR("NAND 8MiB 3,3V 8-bit"), 0xd6, 512, 8, 0x2000, 0},
+
+ {__NANDSTR("NAND 8MiB 1,8V 8-bit"), 0x39, 512, 8, 0x2000, 0},
+ {__NANDSTR("NAND 8MiB 3,3V 8-bit"), 0xe6, 512, 8, 0x2000, 0},
+ {__NANDSTR("NAND 8MiB 1,8V 16-bit"), 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 8MiB 3,3V 16-bit"), 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
#endif
- {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
- {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
- {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 16MiB 1,8V 8-bit"), 0x33, 512, 16, 0x4000, 0},
+ {__NANDSTR("NAND 16MiB 3,3V 8-bit"), 0x73, 512, 16, 0x4000, 0},
+ {__NANDSTR("NAND 16MiB 1,8V 16-bit"), 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 16MiB 3,3V 16-bit"), 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0},
- {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0},
- {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 32MiB 1,8V 8-bit"), 0x35, 512, 32, 0x4000, 0},
+ {__NANDSTR("NAND 32MiB 3,3V 8-bit"), 0x75, 512, 32, 0x4000, 0},
+ {__NANDSTR("NAND 32MiB 1,8V 16-bit"), 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 32MiB 3,3V 16-bit"), 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0},
- {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0},
- {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 64MiB 1,8V 8-bit"), 0x36, 512, 64, 0x4000, 0},
+ {__NANDSTR("NAND 64MiB 3,3V 8-bit"), 0x76, 512, 64, 0x4000, 0},
+ {__NANDSTR("NAND 64MiB 1,8V 16-bit"), 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 64MiB 3,3V 16-bit"), 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0},
- {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0},
- {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},
- {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 128MiB 1,8V 8-bit"), 0x78, 512, 128, 0x4000, 0},
+ {__NANDSTR("NAND 128MiB 1,8V 8-bit"), 0x39, 512, 128, 0x4000, 0},
+ {__NANDSTR("NAND 128MiB 3,3V 8-bit"), 0x79, 512, 128, 0x4000, 0},
+ {__NANDSTR("NAND 128MiB 1,8V 16-bit"), 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 128MiB 1,8V 16-bit"), 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 128MiB 3,3V 16-bit"), 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+ {__NANDSTR("NAND 128MiB 3,3V 16-bit"), 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
- {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
+ {__NANDSTR("NAND 256MiB 3,3V 8-bit"), 0x71, 512, 256, 0x4000, 0},
/*
* These are the new chips with large page size. The pagesize and the
@@ -76,40 +83,40 @@ struct nand_flash_dev nand_flash_ids[] = {
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
/*512 Megabit */
- {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
- {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
- {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
- {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 64MiB 1,8V 8-bit"), 0xA2, 0, 64, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 64MiB 3,3V 8-bit"), 0xF2, 0, 64, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 64MiB 1,8V 16-bit"), 0xB2, 0, 64, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 64MiB 3,3V 16-bit"), 0xC2, 0, 64, 0, LP_OPTIONS16},
/* 1 Gigabit */
- {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},
- {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS},
- {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16},
- {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 128MiB 1,8V 8-bit"), 0xA1, 0, 128, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 128MiB 3,3V 8-bit"), 0xF1, 0, 128, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 128MiB 1,8V 16-bit"), 0xB1, 0, 128, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 128MiB 3,3V 16-bit"), 0xC1, 0, 128, 0, LP_OPTIONS16},
/* 2 Gigabit */
- {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},
- {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},
- {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16},
- {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 256MiB 1,8V 8-bit"), 0xAA, 0, 256, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 256MiB 3,3V 8-bit"), 0xDA, 0, 256, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 256MiB 1,8V 16-bit"), 0xBA, 0, 256, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 256MiB 3,3V 16-bit"), 0xCA, 0, 256, 0, LP_OPTIONS16},
/* 4 Gigabit */
- {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS},
- {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS},
- {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16},
- {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 512MiB 1,8V 8-bit"), 0xAC, 0, 512, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 512MiB 3,3V 8-bit"), 0xDC, 0, 512, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 512MiB 1,8V 16-bit"), 0xBC, 0, 512, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 512MiB 3,3V 16-bit"), 0xCC, 0, 512, 0, LP_OPTIONS16},
/* 8 Gigabit */
- {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS},
- {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS},
- {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16},
- {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 1GiB 1,8V 8-bit"), 0xA3, 0, 1024, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 1GiB 3,3V 8-bit"), 0xD3, 0, 1024, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 1GiB 1,8V 16-bit"), 0xB3, 0, 1024, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 1GiB 3,3V 16-bit"), 0xC3, 0, 1024, 0, LP_OPTIONS16},
/* 16 Gigabit */
- {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS},
- {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},
- {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16},
- {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 2GiB 1,8V 8-bit"), 0xA5, 0, 2048, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 2GiB 3,3V 8-bit"), 0xD5, 0, 2048, 0, LP_OPTIONS},
+ {__NANDSTR("NAND 2GiB 1,8V 16-bit"), 0xB5, 0, 2048, 0, LP_OPTIONS16},
+ {__NANDSTR("NAND 2GiB 3,3V 16-bit"), 0xC5, 0, 2048, 0, LP_OPTIONS16},
/*
* Renesas AND 1 Gigabit. Those chips do not support extended id and
@@ -121,7 +128,7 @@ struct nand_flash_dev nand_flash_ids[] = {
* erased in one go There are more speed improvements for reads and
* writes possible, but not implemented now
*/
- {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000,
+ {__NANDSTR("AND 128MiB 3,3V 8-bit"), 0x01, 2048, 128, 0x4000,
NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY |
BBT_AUTO_REFRESH
},
@@ -133,15 +140,15 @@ struct nand_flash_dev nand_flash_ids[] = {
* Manufacturer ID list
*/
struct nand_manufacturers nand_manuf_ids[] = {
- {NAND_MFR_TOSHIBA, "Toshiba"},
- {NAND_MFR_SAMSUNG, "Samsung"},
- {NAND_MFR_FUJITSU, "Fujitsu"},
- {NAND_MFR_NATIONAL, "National"},
- {NAND_MFR_RENESAS, "Renesas"},
- {NAND_MFR_STMICRO, "ST Micro"},
- {NAND_MFR_HYNIX, "Hynix"},
- {NAND_MFR_MICRON, "Micron"},
- {NAND_MFR_AMD, "AMD"},
+ {NAND_MFR_TOSHIBA, __NANDSTR("Toshiba")},
+ {NAND_MFR_SAMSUNG, __NANDSTR("Samsung")},
+ {NAND_MFR_FUJITSU, __NANDSTR("Fujitsu")},
+ {NAND_MFR_NATIONAL, __NANDSTR("National")},
+ {NAND_MFR_RENESAS, __NANDSTR("Renesas")},
+ {NAND_MFR_STMICRO, __NANDSTR("ST Micro")},
+ {NAND_MFR_HYNIX, __NANDSTR("Hynix")},
+ {NAND_MFR_MICRON, __NANDSTR("Micron")},
+ {NAND_MFR_AMD, __NANDSTR("AMD")},
{0x0, "Unknown"}
};
diff --git a/drivers/mtd/nand/nand_swecc.c b/drivers/mtd/nand/nand_swecc.c
new file mode 100644
index 0000000000..93511cbe3d
--- /dev/null
+++ b/drivers/mtd/nand/nand_swecc.c
@@ -0,0 +1,94 @@
+#include <common.h>
+#include <errno.h>
+#include <clock.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/err.h>
+#include <linux/mtd/nand_ecc.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <malloc.h>
+
+#include "nand.h"
+
+/**
+ * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ */
+static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ chip->ecc.read_page_raw(mtd, chip, buf);
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+}
+
+/**
+ * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ */
+#ifdef CONFIG_NAND_WRITE
+static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ const uint8_t *p = buf;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ /* Software ecc calculation */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+ chip->ecc.write_page_raw(mtd, chip, buf);
+}
+#endif
+
+void nand_init_ecc_soft(struct nand_chip *chip)
+{
+ chip->ecc.calculate = nand_calculate_ecc;
+ chip->ecc.correct = nand_correct_data;
+ chip->ecc.read_page = nand_read_page_swecc;
+ chip->ecc.read_oob = nand_read_oob_std;
+#ifdef CONFIG_NAND_WRITE
+ chip->ecc.write_page = nand_write_page_swecc;
+ chip->ecc.write_oob = nand_write_oob_std;
+#endif
+ chip->ecc.size = 256;
+ chip->ecc.bytes = 3;
+}
diff --git a/drivers/mtd/nand/nand_write.c b/drivers/mtd/nand/nand_write.c
new file mode 100644
index 0000000000..89dc47b35d
--- /dev/null
+++ b/drivers/mtd/nand/nand_write.c
@@ -0,0 +1,746 @@
+#include <common.h>
+#include <errno.h>
+#include <clock.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/err.h>
+#include <linux/mtd/nand_ecc.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <module.h>
+
+#include "nand.h"
+
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
+
+/**
+ * nand_write_buf - [DEFAULT] write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * Default write function for 8bit buswith
+ */
+void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+
+ for (i = 0; i < len; i++)
+ writeb(buf[i], chip->IO_ADDR_W);
+}
+
+/**
+ * nand_write_buf16 - [DEFAULT] write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * Default write function for 16bit buswith
+ */
+void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+ u16 *p = (u16 *) buf;
+ len >>= 1;
+
+ for (i = 0; i < len; i++)
+ writew(p[i], chip->IO_ADDR_W);
+
+}
+
+/**
+ * nand_default_block_markbad - [DEFAULT] mark a block bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+ */
+int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd->priv;
+ uint8_t buf[2] = { 0, 0 };
+ int block, ret;
+
+ /* Get block number */
+ block = (int)(ofs >> chip->bbt_erase_shift);
+ if (chip->bbt)
+ chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+ /* Do we have a flash based bad block table ? */
+ if (chip->options & NAND_USE_FLASH_BBT)
+ ret = nand_update_bbt(mtd, ofs);
+ else {
+ /* We write two bytes, so we dont have to mess with 16 bit
+ * access
+ */
+ ofs += mtd->oobsize;
+ chip->ops.len = chip->ops.ooblen = 2;
+ chip->ops.datbuf = NULL;
+ chip->ops.oobbuf = buf;
+ chip->ops.ooboffs = chip->badblockpos & ~0x01;
+
+ ret = nand_do_write_oob(mtd, ofs, &chip->ops);
+ }
+ if (!ret)
+ mtd->ecc_stats.badblocks++;
+
+ return ret;
+}
+
+/**
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @mtd: MTD device structure
+ * Check, if the device is write protected
+ *
+ * The function expects, that the device is already selected
+ */
+static int nand_check_wp(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ /* Check the WP bit */
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+ return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
+}
+
+/**
+ * nand_write_oob_std - [REPLACABLE] the most common OOB data write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int status = 0;
+ const uint8_t *buf = chip->oob_poi;
+ int length = mtd->oobsize;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+ chip->write_buf(mtd, buf, length);
+ /* Send command to program the OOB data */
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ status = chip->waitfunc(mtd, chip);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+/**
+ * nand_write_page_raw - [Intern] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ */
+void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf)
+{
+ chip->write_buf(mtd, buf, mtd->writesize);
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
+
+/**
+ * nand_write_page - [REPLACEABLE] write one page
+ * @mtd: MTD device structure
+ * @chip: NAND chip descriptor
+ * @buf: the data to write
+ * @page: page number to write
+ * @cached: cached programming
+ * @raw: use _raw version of write_page
+ */
+int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int page, int cached, int raw)
+{
+ int status;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+ if (unlikely(raw))
+ chip->ecc.write_page_raw(mtd, chip, buf);
+ else
+ chip->ecc.write_page(mtd, chip, buf);
+
+ /*
+ * Cached progamming disabled for now, Not sure if its worth the
+ * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+ */
+ cached = 0;
+
+ if (!cached || !(chip->options & NAND_CACHEPRG)) {
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+ /*
+ * See if operation failed and additional status checks are
+ * available
+ */
+ if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+ status = chip->errstat(mtd, chip, FL_WRITING, status,
+ page);
+
+ if (status & NAND_STATUS_FAIL) {
+ return -EIO;
+ }
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+ }
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+ /* Send command to read back the data */
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+ if (chip->verify_buf(mtd, buf, mtd->writesize))
+ return -EIO;
+#endif
+ return 0;
+}
+
+/**
+ * nand_fill_oob - [Internal] Transfer client buffer to oob
+ * @chip: nand chip structure
+ * @oob: oob data buffer
+ * @ops: oob ops structure
+ */
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
+ struct mtd_oob_ops *ops)
+{
+ size_t len = ops->ooblen;
+
+ switch(ops->mode) {
+
+ case MTD_OOB_PLACE:
+ case MTD_OOB_RAW:
+ memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+ return oob + len;
+
+ case MTD_OOB_AUTO: {
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ uint32_t boffs = 0, woffs = ops->ooboffs;
+ size_t bytes = 0;
+
+ for(; free->length && len; free++, len -= bytes) {
+ /* Write request not from offset 0 ? */
+ if (unlikely(woffs)) {
+ if (woffs >= free->length) {
+ woffs -= free->length;
+ continue;
+ }
+ boffs = free->offset + woffs;
+ bytes = min_t(size_t, len,
+ (free->length - woffs));
+ woffs = 0;
+ } else {
+ bytes = min_t(size_t, len, free->length);
+ boffs = free->offset;
+ }
+ memcpy(chip->oob_poi + boffs, oob, bytes);
+ oob += bytes;
+ }
+ return oob;
+ }
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
+
+/**
+ * nand_do_write_ops - [Internal] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
+ *
+ * NAND write with ECC
+ */
+int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, realpage, page, blockmask, column;
+ struct nand_chip *chip = mtd->priv;
+ uint32_t writelen = ops->len;
+ uint8_t *oob = ops->oobbuf;
+ uint8_t *buf = ops->datbuf;
+ int ret, subpage;
+
+ ops->retlen = 0;
+ if (!writelen)
+ return 0;
+
+ /* reject writes, which are not page aligned */
+ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
+ printk(KERN_NOTICE "nand_write: "
+ "Attempt to write not page aligned data\n");
+ return -EINVAL;
+ }
+
+ column = to & (mtd->writesize - 1);
+ subpage = column || (writelen & (mtd->writesize - 1));
+
+ if (subpage && oob)
+ return -EINVAL;
+
+ chipnr = (int)(to >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ return -EIO;
+ }
+
+ realpage = (int)(to >> chip->page_shift);
+ page = realpage & chip->pagemask;
+ blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+
+ /* Invalidate the page cache, when we write to the cached page */
+ if (to <= (chip->pagebuf << chip->page_shift) &&
+ (chip->pagebuf << chip->page_shift) < (to + ops->len))
+ chip->pagebuf = -1;
+
+ /* If we're not given explicit OOB data, let it be 0xFF */
+ if (likely(!oob))
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+ while(1) {
+ int bytes = mtd->writesize;
+ int cached = writelen > bytes && page != blockmask;
+ uint8_t *wbuf = buf;
+
+ /* Partial page write ? */
+ if (unlikely(column || writelen < (mtd->writesize - 1))) {
+ cached = 0;
+ bytes = min_t(int, bytes - column, (int) writelen);
+ chip->pagebuf = -1;
+ memset(chip->buffers->databuf, 0xff, mtd->writesize);
+ memcpy(&chip->buffers->databuf[column], buf, bytes);
+ wbuf = chip->buffers->databuf;
+ }
+
+ if (unlikely(oob))
+ oob = nand_fill_oob(chip, oob, ops);
+
+ ret = chip->write_page(mtd, chip, wbuf, page, cached,
+ (ops->mode == MTD_OOB_RAW));
+ if (ret)
+ break;
+
+ writelen -= bytes;
+ if (!writelen)
+ break;
+
+ column = 0;
+ buf += bytes;
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+ }
+ }
+
+ ops->retlen = ops->len - writelen;
+ if (unlikely(oob))
+ ops->oobretlen = ops->ooblen;
+ return ret;
+}
+
+/**
+ * nand_write - [MTD Interface] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @retlen: pointer to variable to store the number of written bytes
+ * @buf: the data to write
+ *
+ * NAND write with ECC
+ */
+int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const uint8_t *buf)
+{
+ struct nand_chip *chip = mtd->priv;
+ int ret;
+
+ /* Do not allow reads past end of device */
+ if ((to + len) > mtd->size)
+ return -EINVAL;
+ if (!len)
+ return 0;
+
+ chip->ops.len = len;
+ chip->ops.datbuf = (uint8_t *)buf;
+ chip->ops.oobbuf = NULL;
+
+ ret = nand_do_write_ops(mtd, to, &chip->ops);
+
+ *retlen = chip->ops.retlen;
+
+ return ret;
+}
+
+/**
+ * nand_do_write_oob - [MTD Interface] NAND write out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ *
+ * NAND write out-of-band
+ */
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, page, status, len;
+ struct nand_chip *chip = mtd->priv;
+
+ MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
+ (unsigned int)to, (int)ops->ooblen);
+
+ if (ops->mode == MTD_OOB_AUTO)
+ len = chip->ecc.layout->oobavail;
+ else
+ len = mtd->oobsize;
+
+ /* Do not allow write past end of page */
+ if ((ops->ooboffs + ops->ooblen) > len) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
+ "Attempt to write past end of page\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(ops->ooboffs >= len)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt to start write outside oob\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size ||
+ ops->ooboffs + ops->ooblen >
+ ((mtd->size >> chip->page_shift) -
+ (to >> chip->page_shift)) * len)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt write beyond end of device\n");
+ return -EINVAL;
+ }
+
+ chipnr = (int)(to >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ page = (int)(to >> chip->page_shift);
+
+ /*
+ * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
+ * of my DiskOnChip 2000 test units) will clear the whole data page too
+ * if we don't do this. I have no clue why, but I seem to have 'fixed'
+ * it in the doc2000 driver in August 1999. dwmw2.
+ */
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd))
+ return -EROFS;
+
+ /* Invalidate the page cache, if we write to the cached page */
+ if (page == chip->pagebuf)
+ chip->pagebuf = -1;
+
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ nand_fill_oob(chip, ops->oobbuf, ops);
+ status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+ if (status)
+ return status;
+
+ ops->oobretlen = ops->ooblen;
+
+ return 0;
+}
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ */
+int nand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int ret = -ENOSYS;
+
+ ops->retlen = 0;
+
+ /* Do not allow writes past end of device */
+ if (ops->datbuf && (to + ops->len) > mtd->size) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt read beyond end of device\n");
+ return -EINVAL;
+ }
+
+ switch(ops->mode) {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ case MTD_OOB_RAW:
+ break;
+
+ default:
+ goto out;
+ }
+
+ if (!ops->datbuf)
+ ret = nand_do_write_oob(mtd, to, ops);
+ else
+ ret = nand_do_write_ops(mtd, to, ops);
+
+ out:
+ return ret;
+}
+
+/**
+ * single_erease_cmd - [GENERIC] NAND standard block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
+ *
+ * Standard erase command for NAND chips
+ */
+void single_erase_cmd(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *chip = mtd->priv;
+ /* Send commands to erase a block */
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * multi_erease_cmd - [GENERIC] AND specific block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
+ *
+ * AND multi block erase command function
+ * Erase 4 consecutive blocks
+ */
+void multi_erase_cmd(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *chip = mtd->priv;
+ /* Send commands to erase a block */
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * nand_erase - [MTD Interface] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ *
+ * Erase one ore more blocks
+ */
+int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ return nand_erase_nand(mtd, instr, 0);
+}
+
+#define BBT_PAGE_MASK 0xffffff3f
+/**
+ * nand_erase_nand - [Internal] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ * @allowbbt: allow erasing the bbt area
+ *
+ * Erase one ore more blocks
+ */
+int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+ int allowbbt)
+{
+ int page, len, status, pages_per_block, ret, chipnr;
+ struct nand_chip *chip = mtd->priv;
+ int rewrite_bbt[NAND_MAX_CHIPS]={0};
+ unsigned int bbt_masked_page = 0xffffffff;
+
+ MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n",
+ (unsigned int)instr->addr, (unsigned int)instr->len);
+
+ /* Start address must align on block boundary */
+ if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
+ return -EINVAL;
+ }
+
+ /* Length must align on block boundary */
+ if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+ "Length not block aligned\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow erase past end of device */
+ if ((instr->len + instr->addr) > mtd->size) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+ "Erase past end of device\n");
+ return -EINVAL;
+ }
+
+ instr->fail_addr = 0xffffffff;
+
+ /* Shift to get first page */
+ page = (int)(instr->addr >> chip->page_shift);
+ chipnr = (int)(instr->addr >> chip->chip_shift);
+
+ /* Calculate pages in each block */
+ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
+
+ /* Select the NAND device */
+ chip->select_chip(mtd, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+ "Device is write protected!!!\n");
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+
+ /*
+ * If BBT requires refresh, set the BBT page mask to see if the BBT
+ * should be rewritten. Otherwise the mask is set to 0xffffffff which
+ * can not be matched. This is also done when the bbt is actually
+ * erased to avoid recusrsive updates
+ */
+ if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
+ bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
+
+ /* Loop through the pages */
+ len = instr->len;
+
+ instr->state = MTD_ERASING;
+
+ while (len) {
+ /*
+ * heck if we have a bad block, we do not erase bad blocks !
+ */
+ if (nand_block_checkbad(mtd, ((loff_t) page) <<
+ chip->page_shift, 0, allowbbt)) {
+ printk(KERN_WARNING "nand_erase: attempt to erase a "
+ "bad block at page 0x%08x\n", page);
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+
+ /*
+ * Invalidate the page cache, if we erase the block which
+ * contains the current cached page
+ */
+ if (page <= chip->pagebuf && chip->pagebuf <
+ (page + pages_per_block))
+ chip->pagebuf = -1;
+
+ chip->erase_cmd(mtd, page & chip->pagemask);
+
+ status = chip->waitfunc(mtd, chip);
+
+ /*
+ * See if operation failed and additional status checks are
+ * available
+ */
+ if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+ status = chip->errstat(mtd, chip, FL_ERASING,
+ status, page);
+
+ /* See if block erase succeeded */
+ if (status & NAND_STATUS_FAIL) {
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
+ "Failed erase, page 0x%08x\n", page);
+ instr->state = MTD_ERASE_FAILED;
+ instr->fail_addr = (page << chip->page_shift);
+ goto erase_exit;
+ }
+
+ /*
+ * If BBT requires refresh, set the BBT rewrite flag to the
+ * page being erased
+ */
+ if (bbt_masked_page != 0xffffffff &&
+ (page & BBT_PAGE_MASK) == bbt_masked_page)
+ rewrite_bbt[chipnr] = (page << chip->page_shift);
+
+ /* Increment page address and decrement length */
+ len -= (1 << chip->phys_erase_shift);
+ page += pages_per_block;
+
+ /* Check, if we cross a chip boundary */
+ if (len && !(page & chip->pagemask)) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+
+ /*
+ * If BBT requires refresh and BBT-PERCHIP, set the BBT
+ * page mask to see if this BBT should be rewritten
+ */
+ if (bbt_masked_page != 0xffffffff &&
+ (chip->bbt_td->options & NAND_BBT_PERCHIP))
+ bbt_masked_page = chip->bbt_td->pages[chipnr] &
+ BBT_PAGE_MASK;
+ }
+ }
+ instr->state = MTD_ERASE_DONE;
+
+ erase_exit:
+
+ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+
+ /* Do call back function */
+ if (!ret)
+ mtd_erase_callback(instr);
+
+ /*
+ * If BBT requires refresh and erase was successful, rewrite any
+ * selected bad block tables
+ */
+ if (bbt_masked_page == 0xffffffff || ret)
+ return ret;
+
+ for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
+ if (!rewrite_bbt[chipnr])
+ continue;
+ /* update the BBT for chip */
+ MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "
+ "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
+ chip->bbt_td->pages[chipnr]);
+ nand_update_bbt(mtd, rewrite_bbt[chipnr]);
+ }
+
+ /* Return more or less happy */
+ return ret;
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ */
+int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd->priv;
+ int ret;
+
+ if ((ret = nand_block_isbad(mtd, ofs))) {
+ /* If it was bad already, return success and do nothing. */
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ return chip->block_markbad(mtd, ofs);
+}