aboutsummaryrefslogtreecommitdiff
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/Kconfig63
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/cafe.c25
-rw-r--r--drivers/mtd/nand/cmx270_nand.c267
-rw-r--r--drivers/mtd/nand/nand_base.c8
-rw-r--r--drivers/mtd/nand/nand_ids.c4
-rw-r--r--drivers/mtd/nand/nandsim.c518
7 files changed, 816 insertions, 70 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2d12dcdd740..d05873b8c15 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,10 +1,7 @@
# drivers/mtd/nand/Kconfig
# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $
-menu "NAND Flash Device Drivers"
- depends on MTD!=n
-
-config MTD_NAND
+menuconfig MTD_NAND
tristate "NAND Device Support"
depends on MTD
select MTD_NAND_IDS
@@ -13,9 +10,10 @@ config MTD_NAND
devices. For further information see
<http://www.linux-mtd.infradead.org/doc/nand.html>.
+if MTD_NAND
+
config MTD_NAND_VERIFY_WRITE
bool "Verify NAND page writes"
- depends on MTD_NAND
help
This adds an extra check when data is written to the flash. The
NAND flash device internally checks only bits transitioning
@@ -25,53 +23,61 @@ config MTD_NAND_VERIFY_WRITE
config MTD_NAND_ECC_SMC
bool "NAND ECC Smart Media byte order"
- depends on MTD_NAND
default n
help
Software ECC according to the Smart Media Specification.
The original Linux implementation had byte 0 and 1 swapped.
+config MTD_NAND_MUSEUM_IDS
+ bool "Enable chip ids for obsolete ancient NAND devices"
+ depends on MTD_NAND
+ default n
+ help
+ Enable this option only when your board has first generation
+ NAND chips (page size 256 byte, erase size 4-8KiB). The IDs
+ of these chips were reused by later, larger chips.
+
config MTD_NAND_AUTCPU12
tristate "SmartMediaCard on autronix autcpu12 board"
- depends on MTD_NAND && ARCH_AUTCPU12
+ depends on ARCH_AUTCPU12
help
This enables the driver for the autronix autcpu12 board to
access the SmartMediaCard.
config MTD_NAND_EDB7312
tristate "Support for Cirrus Logic EBD7312 evaluation board"
- depends on MTD_NAND && ARCH_EDB7312
+ depends on ARCH_EDB7312
help
This enables the driver for the Cirrus Logic EBD7312 evaluation
board to access the onboard NAND Flash.
config MTD_NAND_H1900
tristate "iPAQ H1900 flash"
- depends on MTD_NAND && ARCH_PXA && MTD_PARTITIONS
+ depends on ARCH_PXA && MTD_PARTITIONS
help
This enables the driver for the iPAQ h1900 flash.
config MTD_NAND_SPIA
tristate "NAND Flash device on SPIA board"
- depends on ARCH_P720T && MTD_NAND
+ depends on ARCH_P720T
help
If you had to ask, you don't have one. Say 'N'.
config MTD_NAND_AMS_DELTA
tristate "NAND Flash device on Amstrad E3"
- depends on MACH_AMS_DELTA && MTD_NAND
+ depends on MACH_AMS_DELTA
help
Support for NAND flash on Amstrad E3 (Delta).
config MTD_NAND_TOTO
tristate "NAND Flash device on TOTO board"
- depends on ARCH_OMAP && MTD_NAND && BROKEN
+ depends on ARCH_OMAP && BROKEN
help
Support for NAND flash on Texas Instruments Toto platform.
config MTD_NAND_TS7250
tristate "NAND Flash device on TS-7250 board"
- depends on MACH_TS72XX && MTD_NAND
+ depends on MACH_TS72XX
help
Support for NAND flash on Technologic Systems TS-7250 platform.
@@ -80,14 +86,14 @@ config MTD_NAND_IDS
config MTD_NAND_AU1550
tristate "Au1550/1200 NAND support"
- depends on (SOC_AU1200 || SOC_AU1550) && MTD_NAND
+ depends on SOC_AU1200 || SOC_AU1550
help
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
config MTD_NAND_RTC_FROM4
tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
- depends on MTD_NAND && SH_SOLUTION_ENGINE
+ depends on SH_SOLUTION_ENGINE
select REED_SOLOMON
select REED_SOLOMON_DEC8
select BITREVERSE
@@ -97,13 +103,13 @@ config MTD_NAND_RTC_FROM4
config MTD_NAND_PPCHAMELEONEVB
tristate "NAND Flash device on PPChameleonEVB board"
- depends on PPCHAMELEONEVB && MTD_NAND && BROKEN
+ depends on PPCHAMELEONEVB && BROKEN
help
This enables the NAND flash driver on the PPChameleon EVB Board.
config MTD_NAND_S3C2410
tristate "NAND Flash support for S3C2410/S3C2440 SoC"
- depends on ARCH_S3C2410 && MTD_NAND
+ depends on ARCH_S3C2410
help
This enables the NAND flash controller on the S3C2410 and S3C2440
SoCs
@@ -128,7 +134,7 @@ config MTD_NAND_S3C2410_HWECC
config MTD_NAND_NDFC
tristate "NDFC NanD Flash Controller"
- depends on MTD_NAND && 44x
+ depends on 44x
select MTD_NAND_ECC_SMC
help
NDFC Nand Flash Controllers are integrated in EP44x SoCs
@@ -145,7 +151,7 @@ config MTD_NAND_S3C2410_CLKSTOP
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
- depends on MTD_NAND && EXPERIMENTAL
+ depends on EXPERIMENTAL
select REED_SOLOMON
select REED_SOLOMON_DEC16
help
@@ -215,11 +221,11 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
- depends on MTD_NAND && ARCH_PXA
+ depends on ARCH_PXA
config MTD_NAND_BASLER_EXCITE
tristate "Support for NAND Flash on Basler eXcite"
- depends on MTD_NAND && BASLER_EXCITE
+ depends on BASLER_EXCITE
help
This enables the driver for the NAND flash device found on the
Basler eXcite Smart Camera. If built as a module, the driver
@@ -227,14 +233,14 @@ config MTD_NAND_BASLER_EXCITE
config MTD_NAND_CAFE
tristate "NAND support for OLPC CAFÉ chip"
- depends on MTD_NAND && PCI
+ depends on PCI
help
Use NAND flash attached to the CAFÉ chip designed for the $100
laptop.
config MTD_NAND_CS553X
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
- depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH)
+ depends on X86_32 && (X86_PC || X86_GENERICARCH)
help
The CS553x companion chips for the AMD Geode processor
include NAND flash controllers with built-in hardware ECC
@@ -247,16 +253,21 @@ config MTD_NAND_CS553X
config MTD_NAND_AT91
bool "Support for NAND Flash / SmartMedia on AT91"
- depends on MTD_NAND && ARCH_AT91
+ depends on ARCH_AT91
help
Enables support for NAND Flash / Smart Media Card interface
on Atmel AT91 processors.
+config MTD_NAND_CM_X270
+ tristate "Support for NAND Flash on CM-X270 modules"
+ depends on MTD_NAND && MACH_ARMCORE
+
+
config MTD_NAND_NANDSIM
tristate "Support for NAND Flash Simulator"
- depends on MTD_NAND && MTD_PARTITIONS
+ depends on MTD_PARTITIONS
help
The simulator may simulate various NAND flash chips for the
MTD nand layer.
-endmenu
+endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 80f1dfc7794..6872031a3fb 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
+obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c
index fd6bb3ed40d..c328a751451 100644
--- a/drivers/mtd/nand/cafe.c
+++ b/drivers/mtd/nand/cafe.c
@@ -530,7 +530,6 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
{
struct mtd_info *mtd;
struct cafe_priv *cafe;
- uint32_t timing1, timing2, timing3;
uint32_t ctrl;
int err = 0;
@@ -587,21 +586,19 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
}
if (numtimings == 3) {
- timing1 = timing[0];
- timing2 = timing[1];
- timing3 = timing[2];
cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
- timing1, timing2, timing3);
+ timing[0], timing[1], timing[2]);
} else {
- timing1 = cafe_readl(cafe, NAND_TIMING1);
- timing2 = cafe_readl(cafe, NAND_TIMING2);
- timing3 = cafe_readl(cafe, NAND_TIMING3);
+ timing[0] = cafe_readl(cafe, NAND_TIMING1);
+ timing[1] = cafe_readl(cafe, NAND_TIMING2);
+ timing[2] = cafe_readl(cafe, NAND_TIMING3);
- if (timing1 | timing2 | timing3) {
- cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3);
+ if (timing[0] | timing[1] | timing[2]) {
+ cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n",
+ timing[0], timing[1], timing[2]);
} else {
dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
- timing1 = timing2 = timing3 = 0xffffffff;
+ timing[0] = timing[1] = timing[2] = 0xffffffff;
}
}
@@ -609,9 +606,9 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
cafe_writel(cafe, 1, NAND_RESET);
cafe_writel(cafe, 0, NAND_RESET);
- cafe_writel(cafe, timing1, NAND_TIMING1);
- cafe_writel(cafe, timing2, NAND_TIMING2);
- cafe_writel(cafe, timing3, NAND_TIMING3);
+ cafe_writel(cafe, timing[0], NAND_TIMING1);
+ cafe_writel(cafe, timing[1], NAND_TIMING2);
+ cafe_writel(cafe, timing[2], NAND_TIMING3);
cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c
new file mode 100644
index 00000000000..cb663ef245d
--- /dev/null
+++ b/drivers/mtd/nand/cmx270_nand.c
@@ -0,0 +1,267 @@
+/*
+ * linux/drivers/mtd/nand/cmx270-nand.c
+ *
+ * Copyright (C) 2006 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Derived from drivers/mtd/nand/h1910.c
+ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
+ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device found on the
+ * CM-X270 board.
+ */
+
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#define GPIO_NAND_CS (11)
+#define GPIO_NAND_RB (89)
+
+/* This macro needed to ensure in-order operation of GPIO and local
+ * bus. Without both asm command and dummy uncached read there're
+ * states when NAND access is broken. I've looked for such macro(s) in
+ * include/asm-arm but found nothing approptiate.
+ * dmac_clean_range is close, but is makes cache invalidation
+ * unnecessary here and it cannot be used in module
+ */
+#define DRAIN_WB() \
+ do { \
+ unsigned char dummy; \
+ asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0"); \
+ dummy=*((unsigned char*)UNCACHED_ADDR); \
+ } while(0)
+
+/* MTD structure for CM-X270 board */
+static struct mtd_info *cmx270_nand_mtd;
+
+/* remaped IO address of the device */
+static void __iomem *cmx270_nand_io;
+
+/*
+ * Define static partitions for flash device
+ */
+static struct mtd_partition partition_info[] = {
+ [0] = {
+ .name = "cmx270-0",
+ .offset = 0,
+ .size = MTDPART_SIZ_FULL
+ }
+};
+#define NUM_PARTITIONS (ARRAY_SIZE(partition_info))
+
+const char *part_probes[] = { "cmdlinepart", NULL };
+
+static u_char cmx270_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+
+ return (readl(this->IO_ADDR_R) >> 16);
+}
+
+static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+
+ for (i=0; i<len; i++)
+ writel((*buf++ << 16), this->IO_ADDR_W);
+}
+
+static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+
+ for (i=0; i<len; i++)
+ *buf++ = readl(this->IO_ADDR_R) >> 16;
+}
+
+static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+
+ for (i=0; i<len; i++)
+ if (buf[i] != (u_char)(readl(this->IO_ADDR_R) >> 16))
+ return -EFAULT;
+
+ return 0;
+}
+
+static inline void nand_cs_on(void)
+{
+ GPCR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
+}
+
+static void nand_cs_off(void)
+{
+ DRAIN_WB();
+
+ GPSR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
+}
+
+/*
+ * hardware specific access to control-lines
+ */
+static void cmx270_hwcontrol(struct mtd_info *mtd, int dat,
+ unsigned int ctrl)
+{
+ struct nand_chip* this = mtd->priv;
+ unsigned int nandaddr = (unsigned int)this->IO_ADDR_W;
+
+ DRAIN_WB();
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if ( ctrl & NAND_ALE )
+ nandaddr |= (1 << 3);
+ else
+ nandaddr &= ~(1 << 3);
+ if ( ctrl & NAND_CLE )
+ nandaddr |= (1 << 2);
+ else
+ nandaddr &= ~(1 << 2);
+ if ( ctrl & NAND_NCE )
+ nand_cs_on();
+ else
+ nand_cs_off();
+ }
+
+ DRAIN_WB();
+ this->IO_ADDR_W = (void __iomem*)nandaddr;
+ if (dat != NAND_CMD_NONE)
+ writel((dat << 16), this->IO_ADDR_W);
+
+ DRAIN_WB();
+}
+
+/*
+ * read device ready pin
+ */
+static int cmx270_device_ready(struct mtd_info *mtd)
+{
+ DRAIN_WB();
+
+ return (GPLR(GPIO_NAND_RB) & GPIO_bit(GPIO_NAND_RB));
+}
+
+/*
+ * Main initialization routine
+ */
+static int cmx270_init(void)
+{
+ struct nand_chip *this;
+ const char *part_type;
+ struct mtd_partition *mtd_parts;
+ int mtd_parts_nb = 0;
+ int ret;
+
+ /* Allocate memory for MTD device structure and private data */
+ cmx270_nand_mtd = kzalloc(sizeof(struct mtd_info) +
+ sizeof(struct nand_chip),
+ GFP_KERNEL);
+ if (!cmx270_nand_mtd) {
+ printk("Unable to allocate CM-X270 NAND MTD device structure.\n");
+ return -ENOMEM;
+ }
+
+ cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12);
+ if (!cmx270_nand_io) {
+ printk("Unable to ioremap NAND device\n");
+ ret = -EINVAL;
+ goto err1;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *)(&cmx270_nand_mtd[1]);
+
+ /* Link the private data with the MTD structure */
+ cmx270_nand_mtd->owner = THIS_MODULE;
+ cmx270_nand_mtd->priv = this;
+
+ /* insert callbacks */
+ this->IO_ADDR_R = cmx270_nand_io;
+ this->IO_ADDR_W = cmx270_nand_io;
+ this->cmd_ctrl = cmx270_hwcontrol;
+ this->dev_ready = cmx270_device_ready;
+
+ /* 15 us command delay time */
+ this->chip_delay = 20;
+ this->ecc.mode = NAND_ECC_SOFT;
+
+ /* read/write functions */
+ this->read_byte = cmx270_read_byte;
+ this->read_buf = cmx270_read_buf;
+ this->write_buf = cmx270_write_buf;
+ this->verify_buf = cmx270_verify_buf;
+
+ /* Scan to find existence of the device */
+ if (nand_scan (cmx270_nand_mtd, 1)) {
+ printk(KERN_NOTICE "No NAND device\n");
+ ret = -ENXIO;
+ goto err2;
+ }
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+ mtd_parts_nb = parse_mtd_partitions(cmx270_nand_mtd, part_probes,
+ &mtd_parts, 0);
+ if (mtd_parts_nb > 0)
+ part_type = "command line";
+ else
+ mtd_parts_nb = 0;
+#endif
+ if (!mtd_parts_nb) {
+ mtd_parts = partition_info;
+ mtd_parts_nb = NUM_PARTITIONS;
+ part_type = "static";
+ }
+
+ /* Register the partitions */
+ printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+ ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb);
+ if (ret)
+ goto err2;
+
+ /* Return happy */
+ return 0;
+
+err2:
+ iounmap(cmx270_nand_io);
+err1:
+ kfree(cmx270_nand_mtd);
+
+ return ret;
+
+}
+module_init(cmx270_init);
+
+/*
+ * Clean up routine
+ */
+static void cmx270_cleanup(void)
+{
+ /* Release resources, unregister device */
+ nand_release(cmx270_nand_mtd);
+
+ iounmap(cmx270_nand_io);
+
+ /* Free the MTD device structure */
+ kfree (cmx270_nand_mtd);
+}
+module_exit(cmx270_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module");
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 6af37b8cff6..04de315e493 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -312,7 +312,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
/* Select the NAND device */
chip->select_chip(mtd, chipnr);
} else
- page = (int)ofs;
+ page = (int)(ofs >> chip->page_shift);
if (chip->options & NAND_BUSWIDTH_16) {
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
@@ -350,7 +350,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
int block, ret;
/* Get block number */
- block = ((int)ofs) >> chip->bbt_erase_shift;
+ block = (int)(ofs >> chip->bbt_erase_shift);
if (chip->bbt)
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
@@ -771,7 +771,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *ecc_code = chip->buffers->ecccode;
int *eccpos = chip->ecc.layout->eccpos;
- nand_read_page_raw(mtd, chip, buf);
+ 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]);
@@ -1426,7 +1426,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- nand_write_page_raw(mtd, chip, buf);
+ chip->ecc.write_page_raw(mtd, chip, buf);
}
/**
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 2e2cdf2fc91..2fc674a190c 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -24,6 +24,8 @@
* 512 512 Byte page size
*/
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},
@@ -39,6 +41,7 @@ struct nand_flash_dev nand_flash_ids[] = {
{"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},
+#endif
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
@@ -137,6 +140,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"},
+ {NAND_MFR_MICRON, "Micron"},
{0x0, "Unknown"}
};
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index c3bca9590ad..205df0f771f 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -37,6 +37,8 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/random.h>
/* Default simulator parameters values */
#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \
@@ -90,6 +92,15 @@ static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH;
static uint do_delays = CONFIG_NANDSIM_DO_DELAYS;
static uint log = CONFIG_NANDSIM_LOG;
static uint dbg = CONFIG_NANDSIM_DBG;
+static unsigned long parts[MAX_MTD_DEVICES];
+static unsigned int parts_num;
+static char *badblocks = NULL;
+static char *weakblocks = NULL;
+static char *weakpages = NULL;
+static unsigned int bitflips = 0;
+static char *gravepages = NULL;
+static unsigned int rptwear = 0;
+static unsigned int overridesize = 0;
module_param(first_id_byte, uint, 0400);
module_param(second_id_byte, uint, 0400);
@@ -104,8 +115,16 @@ module_param(bus_width, uint, 0400);
module_param(do_delays, uint, 0400);
module_param(log, uint, 0400);
module_param(dbg, uint, 0400);
-
-MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)");
+module_param_array(parts, ulong, &parts_num, 0400);
+module_param(badblocks, charp, 0400);
+module_param(weakblocks, charp, 0400);
+module_param(weakpages, charp, 0400);
+module_param(bitflips, uint, 0400);
+module_param(gravepages, charp, 0400);
+module_param(rptwear, uint, 0400);
+module_param(overridesize, uint, 0400);
+
+MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command");
MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
@@ -118,6 +137,23 @@ MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)");
MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero");
MODULE_PARM_DESC(log, "Perform logging if not zero");
MODULE_PARM_DESC(dbg, "Output debug information if not zero");
+MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas");
+/* Page and erase block positions for the following parameters are independent of any partitions */
+MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas");
+MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]"
+ " separated by commas e.g. 113:2 means eb 113"
+ " can be erased only twice before failing");
+MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]"
+ " separated by commas e.g. 1401:2 means page 1401"
+ " can be written only twice before failing");
+MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)");
+MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]"
+ " separated by commas e.g. 1401:2 means page 1401"
+ " can be read only twice before failing");
+MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero");
+MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. "
+ "The size is specified in erase blocks and as the exponent of a power of two"
+ " e.g. 5 means a size of 32 erase blocks");
/* The largest possible page size */
#define NS_LARGEST_PAGE_SIZE 2048
@@ -131,9 +167,11 @@ MODULE_PARM_DESC(dbg, "Output debug information if not zero");
#define NS_DBG(args...) \
do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)
#define NS_WARN(args...) \
- do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0)
+ do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0)
#define NS_ERR(args...) \
- do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0)
+ do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0)
+#define NS_INFO(args...) \
+ do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0)
/* Busy-wait delay macros (microseconds, milliseconds) */
#define NS_UDELAY(us) \
@@ -238,7 +276,8 @@ union ns_mem {
* The structure which describes all the internal simulator data.
*/
struct nandsim {
- struct mtd_partition part;
+ struct mtd_partition partitions[MAX_MTD_DEVICES];
+ unsigned int nbparts;
uint busw; /* flash chip bus width (8 or 16) */
u_char ids[4]; /* chip's ID bytes */
@@ -338,6 +377,38 @@ static struct nandsim_operations {
STATE_DATAOUT, STATE_READY}}
};
+struct weak_block {
+ struct list_head list;
+ unsigned int erase_block_no;
+ unsigned int max_erases;
+ unsigned int erases_done;
+};
+
+static LIST_HEAD(weak_blocks);
+
+struct weak_page {
+ struct list_head list;
+ unsigned int page_no;
+ unsigned int max_writes;
+ unsigned int writes_done;
+};
+
+static LIST_HEAD(weak_pages);
+
+struct grave_page {
+ struct list_head list;
+ unsigned int page_no;
+ unsigned int max_reads;
+ unsigned int reads_done;
+};
+
+static LIST_HEAD(grave_pages);
+
+static unsigned long *erase_block_wear = NULL;
+static unsigned int wear_eb_count = 0;
+static unsigned long total_wear = 0;
+static unsigned int rptwear_cnt = 0;
+
/* MTD structure for NAND controller */
static struct mtd_info *nsmtd;
@@ -381,6 +452,13 @@ static void free_device(struct nandsim *ns)
}
}
+static char *get_partition_name(int i)
+{
+ char buf[64];
+ sprintf(buf, "NAND simulator partition %d", i);
+ return kstrdup(buf, GFP_KERNEL);
+}
+
/*
* Initialize the nandsim structure.
*
@@ -390,7 +468,9 @@ static int init_nandsim(struct mtd_info *mtd)
{
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
struct nandsim *ns = (struct nandsim *)(chip->priv);
- int i;
+ int i, ret = 0;
+ u_int32_t remains;
+ u_int32_t next_offset;
if (NS_IS_INITIALIZED(ns)) {
NS_ERR("init_nandsim: nandsim is already initialized\n");
@@ -448,6 +528,40 @@ static int init_nandsim(struct mtd_info *mtd)
}
}
+ /* Fill the partition_info structure */
+ if (parts_num > ARRAY_SIZE(ns->partitions)) {
+ NS_ERR("too many partitions.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ remains = ns->geom.totsz;
+ next_offset = 0;
+ for (i = 0; i < parts_num; ++i) {
+ unsigned long part = parts[i];
+ if (!part || part > remains / ns->geom.secsz) {
+ NS_ERR("bad partition size.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ ns->partitions[i].name = get_partition_name(i);
+ ns->partitions[i].offset = next_offset;
+ ns->partitions[i].size = part * ns->geom.secsz;
+ next_offset += ns->partitions[i].size;
+ remains -= ns->partitions[i].size;
+ }
+ ns->nbparts = parts_num;
+ if (remains) {
+ if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {
+ NS_ERR("too many partitions.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ ns->partitions[i].name = get_partition_name(i);
+ ns->partitions[i].offset = next_offset;
+ ns->partitions[i].size = remains;
+ ns->nbparts += 1;
+ }
+
/* Detect how many ID bytes the NAND chip outputs */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (second_id_byte != nand_flash_ids[i].id)
@@ -474,7 +588,7 @@ static int init_nandsim(struct mtd_info *mtd)
printk("sector address bytes: %u\n", ns->geom.secaddrbytes);
printk("options: %#x\n", ns->options);
- if (alloc_device(ns) != 0)
+ if ((ret = alloc_device(ns)) != 0)
goto error;
/* Allocate / initialize the internal buffer */
@@ -482,21 +596,17 @@ static int init_nandsim(struct mtd_info *mtd)
if (!ns->buf.byte) {
NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
ns->geom.pgszoob);
+ ret = -ENOMEM;
goto error;
}
memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
- /* Fill the partition_info structure */
- ns->part.name = "NAND simulator partition";
- ns->part.offset = 0;
- ns->part.size = ns->geom.totsz;
-
return 0;
error:
free_device(ns);
- return -ENOMEM;
+ return ret;
}
/*
@@ -510,6 +620,287 @@ static void free_nandsim(struct nandsim *ns)
return;
}
+static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd)
+{
+ char *w;
+ int zero_ok;
+ unsigned int erase_block_no;
+ loff_t offset;
+
+ if (!badblocks)
+ return 0;
+ w = badblocks;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ erase_block_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !erase_block_no) {
+ NS_ERR("invalid badblocks.\n");
+ return -EINVAL;
+ }
+ offset = erase_block_no * ns->geom.secsz;
+ if (mtd->block_markbad(mtd, offset)) {
+ NS_ERR("invalid badblocks.\n");
+ return -EINVAL;
+ }
+ if (*w == ',')
+ w += 1;
+ } while (*w);
+ return 0;
+}
+
+static int parse_weakblocks(void)
+{
+ char *w;
+ int zero_ok;
+ unsigned int erase_block_no;
+ unsigned int max_erases;
+ struct weak_block *wb;
+
+ if (!weakblocks)
+ return 0;
+ w = weakblocks;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ erase_block_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !erase_block_no) {
+ NS_ERR("invalid weakblocks.\n");
+ return -EINVAL;
+ }
+ max_erases = 3;
+ if (*w == ':') {
+ w += 1;
+ max_erases = simple_strtoul(w, &w, 0);
+ }
+ if (*w == ',')
+ w += 1;
+ wb = kzalloc(sizeof(*wb), GFP_KERNEL);
+ if (!wb) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ wb->erase_block_no = erase_block_no;
+ wb->max_erases = max_erases;
+ list_add(&wb->list, &weak_blocks);
+ } while (*w);
+ return 0;
+}
+
+static int erase_error(unsigned int erase_block_no)
+{
+ struct weak_block *wb;
+
+ list_for_each_entry(wb, &weak_blocks, list)
+ if (wb->erase_block_no == erase_block_no) {
+ if (wb->erases_done >= wb->max_erases)
+ return 1;
+ wb->erases_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int parse_weakpages(void)
+{
+ char *w;
+ int zero_ok;
+ unsigned int page_no;
+ unsigned int max_writes;
+ struct weak_page *wp;
+
+ if (!weakpages)
+ return 0;
+ w = weakpages;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ page_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !page_no) {
+ NS_ERR("invalid weakpagess.\n");
+ return -EINVAL;
+ }
+ max_writes = 3;
+ if (*w == ':') {
+ w += 1;
+ max_writes = simple_strtoul(w, &w, 0);
+ }
+ if (*w == ',')
+ w += 1;
+ wp = kzalloc(sizeof(*wp), GFP_KERNEL);
+ if (!wp) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ wp->page_no = page_no;
+ wp->max_writes = max_writes;
+ list_add(&wp->list, &weak_pages);
+ } while (*w);
+ return 0;
+}
+
+static int write_error(unsigned int page_no)
+{
+ struct weak_page *wp;
+
+ list_for_each_entry(wp, &weak_pages, list)
+ if (wp->page_no == page_no) {
+ if (wp->writes_done >= wp->max_writes)
+ return 1;
+ wp->writes_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int parse_gravepages(void)
+{
+ char *g;
+ int zero_ok;
+ unsigned int page_no;
+ unsigned int max_reads;
+ struct grave_page *gp;
+
+ if (!gravepages)
+ return 0;
+ g = gravepages;
+ do {
+ zero_ok = (*g == '0' ? 1 : 0);
+ page_no = simple_strtoul(g, &g, 0);
+ if (!zero_ok && !page_no) {
+ NS_ERR("invalid gravepagess.\n");
+ return -EINVAL;
+ }
+ max_reads = 3;
+ if (*g == ':') {
+ g += 1;
+ max_reads = simple_strtoul(g, &g, 0);
+ }
+ if (*g == ',')
+ g += 1;
+ gp = kzalloc(sizeof(*gp), GFP_KERNEL);
+ if (!gp) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ gp->page_no = page_no;
+ gp->max_reads = max_reads;
+ list_add(&gp->list, &grave_pages);
+ } while (*g);
+ return 0;
+}
+
+static int read_error(unsigned int page_no)
+{
+ struct grave_page *gp;
+
+ list_for_each_entry(gp, &grave_pages, list)
+ if (gp->page_no == page_no) {
+ if (gp->reads_done >= gp->max_reads)
+ return 1;
+ gp->reads_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static void free_lists(void)
+{
+ struct list_head *pos, *n;
+ list_for_each_safe(pos, n, &weak_blocks) {
+ list_del(pos);
+ kfree(list_entry(pos, struct weak_block, list));
+ }
+ list_for_each_safe(pos, n, &weak_pages) {
+ list_del(pos);
+ kfree(list_entry(pos, struct weak_page, list));
+ }
+ list_for_each_safe(pos, n, &grave_pages) {
+ list_del(pos);
+ kfree(list_entry(pos, struct grave_page, list));
+ }
+ kfree(erase_block_wear);
+}
+
+static int setup_wear_reporting(struct mtd_info *mtd)
+{
+ size_t mem;
+
+ if (!rptwear)
+ return 0;
+ wear_eb_count = mtd->size / mtd->erasesize;
+ mem = wear_eb_count * sizeof(unsigned long);
+ if (mem / sizeof(unsigned long) != wear_eb_count) {
+ NS_ERR("Too many erase blocks for wear reporting\n");
+ return -ENOMEM;
+ }
+ erase_block_wear = kzalloc(mem, GFP_KERNEL);
+ if (!erase_block_wear) {
+ NS_ERR("Too many erase blocks for wear reporting\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void update_wear(unsigned int erase_block_no)
+{
+ unsigned long wmin = -1, wmax = 0, avg;
+ unsigned long deciles[10], decile_max[10], tot = 0;
+ unsigned int i;
+
+ if (!erase_block_wear)
+ return;
+ total_wear += 1;
+ if (total_wear == 0)
+ NS_ERR("Erase counter total overflow\n");
+ erase_block_wear[erase_block_no] += 1;
+ if (erase_block_wear[erase_block_no] == 0)
+ NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no);
+ rptwear_cnt += 1;
+ if (rptwear_cnt < rptwear)
+ return;
+ rptwear_cnt = 0;
+ /* Calc wear stats */
+ for (i = 0; i < wear_eb_count; ++i) {
+ unsigned long wear = erase_block_wear[i];
+ if (wear < wmin)
+ wmin = wear;
+ if (wear > wmax)
+ wmax = wear;
+ tot += wear;
+ }
+ for (i = 0; i < 9; ++i) {
+ deciles[i] = 0;
+ decile_max[i] = (wmax * (i + 1) + 5) / 10;
+ }
+ deciles[9] = 0;
+ decile_max[9] = wmax;
+ for (i = 0; i < wear_eb_count; ++i) {
+ int d;
+ unsigned long wear = erase_block_wear[i];
+ for (d = 0; d < 10; ++d)
+ if (wear <= decile_max[d]) {
+ deciles[d] += 1;
+ break;
+ }
+ }
+ avg = tot / wear_eb_count;
+ /* Output wear report */
+ NS_INFO("*** Wear Report ***\n");
+ NS_INFO("Total numbers of erases: %lu\n", tot);
+ NS_INFO("Number of erase blocks: %u\n", wear_eb_count);
+ NS_INFO("Average number of erases: %lu\n", avg);
+ NS_INFO("Maximum number of erases: %lu\n", wmax);
+ NS_INFO("Minimum number of erases: %lu\n", wmin);
+ for (i = 0; i < 10; ++i) {
+ unsigned long from = (i ? decile_max[i - 1] + 1 : 0);
+ if (from > decile_max[i])
+ continue;
+ NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n",
+ from,
+ decile_max[i],
+ deciles[i]);
+ }
+ NS_INFO("*** End of Wear Report ***\n");
+}
+
/*
* Returns the string representation of 'state' state.
*/
@@ -822,9 +1213,31 @@ static void read_page(struct nandsim *ns, int num)
NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
memset(ns->buf.byte, 0xFF, num);
} else {
+ unsigned int page_no = ns->regs.row;
NS_DBG("read_page: page %d allocated, reading from %d\n",
ns->regs.row, ns->regs.column + ns->regs.off);
+ if (read_error(page_no)) {
+ int i;
+ memset(ns->buf.byte, 0xFF, num);
+ for (i = 0; i < num; ++i)
+ ns->buf.byte[i] = random32();
+ NS_WARN("simulating read error in page %u\n", page_no);
+ return;
+ }
memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
+ if (bitflips && random32() < (1 << 22)) {
+ int flips = 1;
+ if (bitflips > 1)
+ flips = (random32() % (int) bitflips) + 1;
+ while (flips--) {
+ int pos = random32() % (num * 8);
+ ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
+ NS_WARN("read_page: flipping bit %d in page %d "
+ "reading from %d ecc: corrected=%u failed=%u\n",
+ pos, ns->regs.row, ns->regs.column + ns->regs.off,
+ nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
+ }
+ }
}
}
@@ -883,6 +1296,7 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
{
int num;
int busdiv = ns->busw == 8 ? 1 : 2;
+ unsigned int erase_block_no, page_no;
action &= ACTION_MASK;
@@ -942,14 +1356,24 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
ns->regs.column = 0;
+ erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift);
+
NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
ns->regs.row, NS_RAW_OFFSET(ns));
- NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
+ NS_LOG("erase sector %u\n", erase_block_no);
erase_sector(ns);
NS_MDELAY(erase_delay);
+ if (erase_block_wear)
+ update_wear(erase_block_no);
+
+ if (erase_error(erase_block_no)) {
+ NS_WARN("simulating erase failure in erase block %u\n", erase_block_no);
+ return -1;
+ }
+
break;
case ACTION_PRGPAGE:
@@ -972,6 +1396,8 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
if (prog_page(ns, num) == -1)
return -1;
+ page_no = ns->regs.row;
+
NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
NS_LOG("programm page %d\n", ns->regs.row);
@@ -979,6 +1405,11 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
NS_UDELAY(programm_delay);
NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
+ if (write_error(page_no)) {
+ NS_WARN("simulating write failure in page %u\n", page_no);
+ return -1;
+ }
+
break;
case ACTION_ZEROOFF:
@@ -1503,7 +1934,7 @@ static int __init ns_init_module(void)
{
struct nand_chip *chip;
struct nandsim *nand;
- int retval = -ENOMEM;
+ int retval = -ENOMEM, i;
if (bus_width != 8 && bus_width != 16) {
NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
@@ -1533,6 +1964,8 @@ static int __init ns_init_module(void)
chip->verify_buf = ns_nand_verify_buf;
chip->read_word = ns_nand_read_word;
chip->ecc.mode = NAND_ECC_SOFT;
+ /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
+ /* and 'badblocks' parameters to work */
chip->options |= NAND_SKIP_BBTSCAN;
/*
@@ -1557,6 +1990,15 @@ static int __init ns_init_module(void)
nsmtd->owner = THIS_MODULE;
+ if ((retval = parse_weakblocks()) != 0)
+ goto error;
+
+ if ((retval = parse_weakpages()) != 0)
+ goto error;
+
+ if ((retval = parse_gravepages()) != 0)
+ goto error;
+
if ((retval = nand_scan(nsmtd, 1)) != 0) {
NS_ERR("can't register NAND Simulator\n");
if (retval > 0)
@@ -1564,23 +2006,44 @@ static int __init ns_init_module(void)
goto error;
}
- if ((retval = init_nandsim(nsmtd)) != 0) {
- NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
- goto error;
+ if (overridesize) {
+ u_int32_t new_size = nsmtd->erasesize << overridesize;
+ if (new_size >> overridesize != nsmtd->erasesize) {
+ NS_ERR("overridesize is too big\n");
+ goto err_exit;
+ }
+ /* N.B. This relies on nand_scan not doing anything with the size before we change it */
+ nsmtd->size = new_size;
+ chip->chipsize = new_size;
+ chip->chip_shift = ffs(new_size) - 1;
}
- if ((retval = nand_default_bbt(nsmtd)) != 0) {
- free_nandsim(nand);
- goto error;
- }
+ if ((retval = setup_wear_reporting(nsmtd)) != 0)
+ goto err_exit;
+
+ if ((retval = init_nandsim(nsmtd)) != 0)
+ goto err_exit;
- /* Register NAND as one big partition */
- add_mtd_partitions(nsmtd, &nand->part, 1);
+ if ((retval = parse_badblocks(nand, nsmtd)) != 0)
+ goto err_exit;
+
+ if ((retval = nand_default_bbt(nsmtd)) != 0)
+ goto err_exit;
+
+ /* Register NAND partitions */
+ if ((retval = add_mtd_partitions(nsmtd, &nand->partitions[0], nand->nbparts)) != 0)
+ goto err_exit;
return 0;
+err_exit:
+ free_nandsim(nand);
+ nand_release(nsmtd);
+ for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
+ kfree(nand->partitions[i].name);
error:
kfree(nsmtd);
+ free_lists();
return retval;
}
@@ -1593,10 +2056,14 @@ module_init(ns_init_module);
static void __exit ns_cleanup_module(void)
{
struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv);
+ int i;
free_nandsim(ns); /* Free nandsim private resources */
- nand_release(nsmtd); /* Unregisterd drived */
+ nand_release(nsmtd); /* Unregister driver */
+ for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
+ kfree(ns->partitions[i].name);
kfree(nsmtd); /* Free other structures */
+ free_lists();
}
module_exit(ns_cleanup_module);
@@ -1604,4 +2071,3 @@ module_exit(ns_cleanup_module);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Artem B. Bityuckiy");
MODULE_DESCRIPTION ("The NAND flash simulator");
-