From 5cd0be8ec946ee3901e7f651a795225c6badff8f Mon Sep 17 00:00:00 2001 From: Hemant Pedanekar Date: Thu, 1 Oct 2009 19:55:06 +0530 Subject: mtd: nand: davinci: fix to use mask_ale from pdata Correct typo to use mask_ale from platform data when set to non-zero. Signed-off-by: Hemant Pedanekar Signed-off-by: David Woodhouse --- drivers/mtd/nand/davinci_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index f13f5b9afaf7..68cc9247fdb0 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -599,7 +599,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->mask_chipsel = pdata->mask_chipsel; /* use nandboot-capable ALE/CLE masks by default */ - info->mask_ale = pdata->mask_cle ? : MASK_ALE; + info->mask_ale = pdata->mask_ale ? : MASK_ALE; info->mask_cle = pdata->mask_cle ? : MASK_CLE; /* Set address of hardware control function */ -- cgit v1.2.3 From e4af3bf6334341435740fce9ff236ba814686b99 Mon Sep 17 00:00:00 2001 From: Claudio Scordino Date: Wed, 7 Oct 2009 14:27:07 +0200 Subject: mtd: atmel_nand: unused variable removed Unused variable "eccpos" removed from atmel_nand driver. Signed-off-by: Claudio Scordino Signed-off-by: David Woodhouse --- drivers/mtd/nand/atmel_nand.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index f8e9975c86e5..4f62d207b87b 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -192,7 +192,6 @@ static int atmel_nand_calculate(struct mtd_info *mtd, { struct nand_chip *nand_chip = mtd->priv; struct atmel_nand_host *host = nand_chip->priv; - uint32_t *eccpos = nand_chip->ecc.layout->eccpos; unsigned int ecc_value; /* get the first 2 ECC bytes */ -- cgit v1.2.3 From 82613b0da622efbd24cb7b23eb349966802310f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 1 Oct 2009 10:28:21 +0200 Subject: mtd: move mxcnd_remove to .exit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function mxcnd_remove is used only wrapped by __exit_p so define it using __exit. Signed-off-by: Uwe Kleine-König Acked-by: Sam Ravnborg Cc: Sascha Hauer Cc: Vladimir Barinov Cc: Vladimir Barinov Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 65b26d5a5c0d..ef3e1998b591 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1052,7 +1052,7 @@ eclk: return err; } -static int __devexit mxcnd_remove(struct platform_device *pdev) +static int __exit mxcnd_remove(struct platform_device *pdev) { struct mxc_nand_host *host = platform_get_drvdata(pdev); -- cgit v1.2.3 From 2d098a725333990d265dfe4754d1b63032c35afb Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 19 Oct 2009 19:45:29 -0400 Subject: mtd: plat_nand: request memory resource before doing ioremap Add a request_mem_region() before doing the ioremap(). Also, use the resource_size macro instead of doing the end - start + 1 calc by hand. Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 50 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 16 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 4e16c6f5bdd5..8d467315f02b 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -34,7 +34,12 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) { struct platform_nand_data *pdata = pdev->dev.platform_data; struct plat_nand_data *data; - int res = 0; + struct resource *res; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; /* Allocate memory for the device structure (and zero it) */ data = kzalloc(sizeof(struct plat_nand_data), GFP_KERNEL); @@ -43,12 +48,18 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) return -ENOMEM; } - data->io_base = ioremap(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + if (!request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + err = -EBUSY; + goto out_free; + } + + data->io_base = ioremap(res->start, resource_size(res)); if (data->io_base == NULL) { dev_err(&pdev->dev, "ioremap failed\n"); - kfree(data); - return -EIO; + err = -EIO; + goto out_release_io; } data->chip.priv = &data; @@ -74,24 +85,24 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) /* Handle any platform specific setup */ if (pdata->ctrl.probe) { - res = pdata->ctrl.probe(pdev); - if (res) + err = pdata->ctrl.probe(pdev); + if (err) goto out; } /* Scan to find existance of the device */ if (nand_scan(&data->mtd, 1)) { - res = -ENXIO; + err = -ENXIO; goto out; } #ifdef CONFIG_MTD_PARTITIONS if (pdata->chip.part_probe_types) { - res = parse_mtd_partitions(&data->mtd, + err = parse_mtd_partitions(&data->mtd, pdata->chip.part_probe_types, &data->parts, 0); - if (res > 0) { - add_mtd_partitions(&data->mtd, data->parts, res); + if (err > 0) { + add_mtd_partitions(&data->mtd, data->parts, err); return 0; } } @@ -99,14 +110,14 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) pdata->chip.set_parts(data->mtd.size, &pdata->chip); if (pdata->chip.partitions) { data->parts = pdata->chip.partitions; - res = add_mtd_partitions(&data->mtd, data->parts, + err = add_mtd_partitions(&data->mtd, data->parts, pdata->chip.nr_partitions); } else #endif - res = add_mtd_device(&data->mtd); + err = add_mtd_device(&data->mtd); - if (!res) - return res; + if (!err) + return err; nand_release(&data->mtd); out: @@ -114,8 +125,11 @@ out: pdata->ctrl.remove(pdev); platform_set_drvdata(pdev, NULL); iounmap(data->io_base); +out_release_io: + release_mem_region(res->start, resource_size(res)); +out_free: kfree(data); - return res; + return err; } /* @@ -125,6 +139,9 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); struct platform_nand_data *pdata = pdev->dev.platform_data; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); nand_release(&data->mtd); #ifdef CONFIG_MTD_PARTITIONS @@ -134,6 +151,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) if (pdata->ctrl.remove) pdata->ctrl.remove(pdev); iounmap(data->io_base); + release_mem_region(res->start, resource_size(res)); kfree(data); return 0; -- cgit v1.2.3 From 266dead21675aeb89407b1213788cd924353d5e1 Mon Sep 17 00:00:00 2001 From: "Leo (Hao) Chen" Date: Fri, 9 Oct 2009 19:13:08 -0700 Subject: mtd: add bcmring nand driver Signed-off-by: Leo Hao Chen Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 16 ++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/bcm_umi_bch.c | 213 +++++++++++++++ drivers/mtd/nand/bcm_umi_nand.c | 581 ++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/nand_bcm_umi.c | 149 +++++++++++ drivers/mtd/nand/nand_bcm_umi.h | 358 +++++++++++++++++++++++++ 6 files changed, 1318 insertions(+) create mode 100644 drivers/mtd/nand/bcm_umi_bch.c create mode 100644 drivers/mtd/nand/bcm_umi_nand.c create mode 100644 drivers/mtd/nand/nand_bcm_umi.c create mode 100644 drivers/mtd/nand/nand_bcm_umi.h (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 2fda0b615246..34598e9c1a40 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -203,6 +203,22 @@ config MTD_NAND_S3C2410_CLKSTOP when the is NAND chip selected or released, but will save approximately 5mA of power when there is nothing happening. +config MTD_NAND_BCM_UMI + tristate "NAND Flash support for BCM Reference Boards" + depends on ARCH_BCMRING && MTD_NAND + help + This enables the NAND flash controller on the BCM UMI block. + + No board specfic support is done by this driver, each board + must advertise a platform_device for the driver to attach. + +config MTD_NAND_BCM_UMI_HWCS + bool "BCM UMI NAND Hardware CS" + depends on MTD_NAND_BCM_UMI + help + Enable the use of the BCM UMI block's internal CS using NAND. + This should only be used if you know the external NAND CS can toggle. + config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" depends on EXPERIMENTAL diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 6950d3dabf10..460a1f39a8d1 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -42,5 +42,6 @@ obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o +obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/bcm_umi_bch.c b/drivers/mtd/nand/bcm_umi_bch.c new file mode 100644 index 000000000000..a930666d0687 --- /dev/null +++ b/drivers/mtd/nand/bcm_umi_bch.c @@ -0,0 +1,213 @@ +/***************************************************************************** +* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ +#include "nand_bcm_umi.h" + +/* ---- External Variable Declarations ----------------------------------- */ +/* ---- External Function Prototypes ------------------------------------- */ +/* ---- Public Variables ------------------------------------------------- */ +/* ---- Private Constants and Types -------------------------------------- */ + +/* ---- Private Function Prototypes -------------------------------------- */ +static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page); +static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf); + +/* ---- Private Variables ------------------------------------------------ */ + +/* +** nand_hw_eccoob +** New oob placement block for use with hardware ecc generation. +*/ +static struct nand_ecclayout nand_hw_eccoob_512 = { + /* Reserve 5 for BI indicator */ + .oobfree = { +#if (NAND_ECC_NUM_BYTES > 3) + {.offset = 0, .length = 2} +#else + {.offset = 0, .length = 5}, + {.offset = 6, .length = 7} +#endif + } +}; + +/* +** We treat the OOB for a 2K page as if it were 4 512 byte oobs, +** except the BI is at byte 0. +*/ +static struct nand_ecclayout nand_hw_eccoob_2048 = { + /* Reserve 0 as BI indicator */ + .oobfree = { +#if (NAND_ECC_NUM_BYTES > 10) + {.offset = 1, .length = 2}, +#elif (NAND_ECC_NUM_BYTES > 7) + {.offset = 1, .length = 5}, + {.offset = 16, .length = 6}, + {.offset = 32, .length = 6}, + {.offset = 48, .length = 6} +#else + {.offset = 1, .length = 8}, + {.offset = 16, .length = 9}, + {.offset = 32, .length = 9}, + {.offset = 48, .length = 9} +#endif + } +}; + +/* We treat the OOB for a 4K page as if it were 8 512 byte oobs, + * except the BI is at byte 0. */ +static struct nand_ecclayout nand_hw_eccoob_4096 = { + /* Reserve 0 as BI indicator */ + .oobfree = { +#if (NAND_ECC_NUM_BYTES > 10) + {.offset = 1, .length = 2}, + {.offset = 16, .length = 3}, + {.offset = 32, .length = 3}, + {.offset = 48, .length = 3}, + {.offset = 64, .length = 3}, + {.offset = 80, .length = 3}, + {.offset = 96, .length = 3}, + {.offset = 112, .length = 3} +#else + {.offset = 1, .length = 5}, + {.offset = 16, .length = 6}, + {.offset = 32, .length = 6}, + {.offset = 48, .length = 6}, + {.offset = 64, .length = 6}, + {.offset = 80, .length = 6}, + {.offset = 96, .length = 6}, + {.offset = 112, .length = 6} +#endif + } +}; + +/* ---- Private Functions ------------------------------------------------ */ +/* ==== Public Functions ================================================= */ + +/**************************************************************************** +* +* bcm_umi_bch_read_page_hwecc - hardware ecc based page read function +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: buffer to store read data +* +***************************************************************************/ +static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t * buf, + int page) +{ + int sectorIdx = 0; + int eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps; + uint8_t *datap = buf; + uint8_t eccCalc[NAND_ECC_NUM_BYTES]; + int sectorOobSize = mtd->oobsize / eccsteps; + int stat; + + for (sectorIdx = 0; sectorIdx < eccsteps; + sectorIdx++, datap += eccsize) { + if (sectorIdx > 0) { + /* Seek to page location within sector */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize, + -1); + } + + /* Enable hardware ECC before reading the buf */ + nand_bcm_umi_bch_enable_read_hwecc(); + + /* Read in data */ + bcm_umi_nand_read_buf(mtd, datap, eccsize); + + /* Pause hardware ECC after reading the buf */ + nand_bcm_umi_bch_pause_read_ecc_calc(); + + /* Read the OOB ECC */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + sectorIdx * sectorOobSize, -1); + nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc, + NAND_ECC_NUM_BYTES, + chip->oob_poi + + sectorIdx * sectorOobSize); + + /* Correct any ECC detected errors */ + stat = + nand_bcm_umi_bch_correct_page(datap, eccCalc, + NAND_ECC_NUM_BYTES); + + /* Update Stats */ + if (stat < 0) { +#if defined(NAND_BCM_UMI_DEBUG) + printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n", + __func__, sectorIdx); + printk(KERN_WARNING + "%s data %02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + __func__, datap[0], datap[1], datap[2], datap[3], + datap[4], datap[5], datap[6], datap[7]); + printk(KERN_WARNING + "%s ecc %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x " + "%02x %02x %02x\n", + __func__, eccCalc[0], eccCalc[1], eccCalc[2], + eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6], + eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10], + eccCalc[11], eccCalc[12]); + BUG(); +#endif + mtd->ecc_stats.failed++; + } else { +#if defined(NAND_BCM_UMI_DEBUG) + if (stat > 0) { + printk(KERN_INFO + "%s %d correctable_errors detected\n", + __func__, stat); + } +#endif + mtd->ecc_stats.corrected += stat; + } + } + return 0; +} + +/**************************************************************************** +* +* bcm_umi_bch_write_page_hwecc - hardware ecc based page write function +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: data buffer +* +***************************************************************************/ +static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + int sectorIdx = 0; + int eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps; + const uint8_t *datap = buf; + uint8_t *oobp = chip->oob_poi; + int sectorOobSize = mtd->oobsize / eccsteps; + + for (sectorIdx = 0; sectorIdx < eccsteps; + sectorIdx++, datap += eccsize, oobp += sectorOobSize) { + /* Enable hardware ECC before writing the buf */ + nand_bcm_umi_bch_enable_write_hwecc(); + bcm_umi_nand_write_buf(mtd, datap, eccsize); + nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp, + NAND_ECC_NUM_BYTES); + } + + bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); +} diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c new file mode 100644 index 000000000000..087bcd745bb7 --- /dev/null +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -0,0 +1,581 @@ +/***************************************************************************** +* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "nand_bcm_umi.h" + +#include + +#define USE_DMA 1 +#include +#include +#include + +/* ---- External Variable Declarations ----------------------------------- */ +/* ---- External Function Prototypes ------------------------------------- */ +/* ---- Public Variables ------------------------------------------------- */ +/* ---- Private Constants and Types -------------------------------------- */ +static const __devinitconst char gBanner[] = KERN_INFO \ + "BCM UMI MTD NAND Driver: 1.00\n"; + +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +#if NAND_ECC_BCH +static uint8_t scan_ff_pattern[] = { 0xff }; + +static struct nand_bbt_descr largepage_bbt = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern +}; +#endif + +/* +** Preallocate a buffer to avoid having to do this every dma operation. +** This is the size of the preallocated coherent DMA buffer. +*/ +#if USE_DMA +#define DMA_MIN_BUFLEN 512 +#define DMA_MAX_BUFLEN PAGE_SIZE +#define USE_DIRECT_IO(len) (((len) < DMA_MIN_BUFLEN) || \ + ((len) > DMA_MAX_BUFLEN)) + +/* + * The current NAND data space goes from 0x80001900 to 0x80001FFF, + * which is only 0x700 = 1792 bytes long. This is too small for 2K, 4K page + * size NAND flash. Need to break the DMA down to multiple 1Ks. + * + * Need to make sure REG_NAND_DATA_PADDR + DMA_MAX_LEN < 0x80002000 + */ +#define DMA_MAX_LEN 1024 + +#else /* !USE_DMA */ +#define DMA_MIN_BUFLEN 0 +#define DMA_MAX_BUFLEN 0 +#define USE_DIRECT_IO(len) 1 +#endif +/* ---- Private Function Prototypes -------------------------------------- */ +static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len); +static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf, + int len); + +/* ---- Private Variables ------------------------------------------------ */ +static struct mtd_info *board_mtd; +static void __iomem *bcm_umi_io_base; +static void *virtPtr; +static dma_addr_t physPtr; +static struct completion nand_comp; + +/* ---- Private Functions ------------------------------------------------ */ +#if NAND_ECC_BCH +#include "bcm_umi_bch.c" +#else +#include "bcm_umi_hamming.c" +#endif + +#if USE_DMA + +/* Handler called when the DMA finishes. */ +static void nand_dma_handler(DMA_Device_t dev, int reason, void *userData) +{ + complete(&nand_comp); +} + +static int nand_dma_init(void) +{ + int rc; + + rc = dma_set_device_handler(DMA_DEVICE_NAND_MEM_TO_MEM, + nand_dma_handler, NULL); + if (rc != 0) { + printk(KERN_ERR "dma_set_device_handler failed: %d\n", rc); + return rc; + } + + virtPtr = + dma_alloc_coherent(NULL, DMA_MAX_BUFLEN, &physPtr, GFP_KERNEL); + if (virtPtr == NULL) { + printk(KERN_ERR "NAND - Failed to allocate memory for DMA buffer\n"); + return -ENOMEM; + } + + return 0; +} + +static void nand_dma_term(void) +{ + if (virtPtr != NULL) + dma_free_coherent(NULL, DMA_MAX_BUFLEN, virtPtr, physPtr); +} + +static void nand_dma_read(void *buf, int len) +{ + int offset = 0; + int tmp_len = 0; + int len_left = len; + DMA_Handle_t hndl; + + if (virtPtr == NULL) + panic("nand_dma_read: virtPtr == NULL\n"); + + if ((void *)physPtr == NULL) + panic("nand_dma_read: physPtr == NULL\n"); + + hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM); + if (hndl < 0) { + printk(KERN_ERR + "nand_dma_read: unable to allocate dma channel: %d\n", + (int)hndl); + panic("\n"); + } + + while (len_left > 0) { + if (len_left > DMA_MAX_LEN) { + tmp_len = DMA_MAX_LEN; + len_left -= DMA_MAX_LEN; + } else { + tmp_len = len_left; + len_left = 0; + } + + init_completion(&nand_comp); + dma_transfer_mem_to_mem(hndl, REG_NAND_DATA_PADDR, + physPtr + offset, tmp_len); + wait_for_completion(&nand_comp); + + offset += tmp_len; + } + + dma_free_channel(hndl); + + if (buf != NULL) + memcpy(buf, virtPtr, len); +} + +static void nand_dma_write(const void *buf, int len) +{ + int offset = 0; + int tmp_len = 0; + int len_left = len; + DMA_Handle_t hndl; + + if (buf == NULL) + panic("nand_dma_write: buf == NULL\n"); + + if (virtPtr == NULL) + panic("nand_dma_write: virtPtr == NULL\n"); + + if ((void *)physPtr == NULL) + panic("nand_dma_write: physPtr == NULL\n"); + + memcpy(virtPtr, buf, len); + + + hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM); + if (hndl < 0) { + printk(KERN_ERR + "nand_dma_write: unable to allocate dma channel: %d\n", + (int)hndl); + panic("\n"); + } + + while (len_left > 0) { + if (len_left > DMA_MAX_LEN) { + tmp_len = DMA_MAX_LEN; + len_left -= DMA_MAX_LEN; + } else { + tmp_len = len_left; + len_left = 0; + } + + init_completion(&nand_comp); + dma_transfer_mem_to_mem(hndl, physPtr + offset, + REG_NAND_DATA_PADDR, tmp_len); + wait_for_completion(&nand_comp); + + offset += tmp_len; + } + + dma_free_channel(hndl); +} + +#endif + +static int nand_dev_ready(struct mtd_info *mtd) +{ + return nand_bcm_umi_dev_ready(); +} + +/**************************************************************************** +* +* bcm_umi_nand_inithw +* +* This routine does the necessary hardware (board-specific) +* initializations. This includes setting up the timings, etc. +* +***************************************************************************/ +int bcm_umi_nand_inithw(void) +{ + /* Configure nand timing parameters */ + REG_UMI_NAND_TCR &= ~0x7ffff; + REG_UMI_NAND_TCR |= HW_CFG_NAND_TCR; + +#if !defined(CONFIG_MTD_NAND_BCM_UMI_HWCS) + /* enable software control of CS */ + REG_UMI_NAND_TCR |= REG_UMI_NAND_TCR_CS_SWCTRL; +#endif + + /* keep NAND chip select asserted */ + REG_UMI_NAND_RCSR |= REG_UMI_NAND_RCSR_CS_ASSERTED; + + REG_UMI_NAND_TCR &= ~REG_UMI_NAND_TCR_WORD16; + /* enable writes to flash */ + REG_UMI_MMD_ICR |= REG_UMI_MMD_ICR_FLASH_WP; + + writel(NAND_CMD_RESET, bcm_umi_io_base + REG_NAND_CMD_OFFSET); + nand_bcm_umi_wait_till_ready(); + +#if NAND_ECC_BCH + nand_bcm_umi_bch_config_ecc(NAND_ECC_NUM_BYTES); +#endif + + return 0; +} + +/* Used to turn latch the proper register for access. */ +static void bcm_umi_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + /* send command to hardware */ + struct nand_chip *chip = mtd->priv; + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_CLE) { + chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_CMD_OFFSET; + goto CMD; + } + if (ctrl & NAND_ALE) { + chip->IO_ADDR_W = + bcm_umi_io_base + REG_NAND_ADDR_OFFSET; + goto CMD; + } + chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET; + } + +CMD: + /* Send command to chip directly */ + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); +} + +static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf, + int len) +{ + if (USE_DIRECT_IO(len)) { + /* Do it the old way if the buffer is small or too large. + * Probably quicker than starting and checking dma. */ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + writeb(buf[i], this->IO_ADDR_W); + } +#if USE_DMA + else + nand_dma_write(buf, len); +#endif +} + +static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + if (USE_DIRECT_IO(len)) { + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = readb(this->IO_ADDR_R); + } +#if USE_DMA + else + nand_dma_read(buf, len); +#endif +} + +static uint8_t readbackbuf[NAND_MAX_PAGESIZE]; +static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, + int len) +{ + /* + * Try to readback page with ECC correction. This is necessary + * for MLC parts which may have permanently stuck bits. + */ + struct nand_chip *chip = mtd->priv; + int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0); + if (ret < 0) + return -EFAULT; + else { + if (memcmp(readbackbuf, buf, len) == 0) + return 0; + + return -EFAULT; + } + return 0; +} + +static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct resource *r; + int err = 0; + + printk(gBanner); + + /* Allocate memory for MTD device structure and private data */ + board_mtd = + kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), + GFP_KERNEL); + if (!board_mtd) { + printk(KERN_WARNING + "Unable to allocate NAND MTD device structure.\n"); + return -ENOMEM; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!r) + return -ENXIO; + + /* map physical adress */ + bcm_umi_io_base = ioremap(r->start, r->end - r->start + 1); + + if (!bcm_umi_io_base) { + printk(KERN_ERR "ioremap to access BCM UMI NAND chip failed\n"); + kfree(board_mtd); + return -EIO; + } + + /* Get pointer to private data */ + this = (struct nand_chip *)(&board_mtd[1]); + + /* Initialize structures */ + memset((char *)board_mtd, 0, sizeof(struct mtd_info)); + memset((char *)this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + board_mtd->priv = this; + + /* Initialize the NAND hardware. */ + if (bcm_umi_nand_inithw() < 0) { + printk(KERN_ERR "BCM UMI NAND chip could not be initialized\n"); + iounmap(bcm_umi_io_base); + kfree(board_mtd); + return -EIO; + } + + /* Set address of NAND IO lines */ + this->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET; + this->IO_ADDR_R = bcm_umi_io_base + REG_NAND_DATA8_OFFSET; + + /* Set command delay time, see datasheet for correct value */ + this->chip_delay = 0; + /* Assign the device ready function, if available */ + this->dev_ready = nand_dev_ready; + this->options = 0; + + this->write_buf = bcm_umi_nand_write_buf; + this->read_buf = bcm_umi_nand_read_buf; + this->verify_buf = bcm_umi_nand_verify_buf; + + this->cmd_ctrl = bcm_umi_nand_hwcontrol; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; + this->ecc.bytes = NAND_ECC_NUM_BYTES; +#if NAND_ECC_BCH + this->ecc.read_page = bcm_umi_bch_read_page_hwecc; + this->ecc.write_page = bcm_umi_bch_write_page_hwecc; +#else + this->ecc.correct = nand_correct_data512; + this->ecc.calculate = bcm_umi_hamming_get_hw_ecc; + this->ecc.hwctl = bcm_umi_hamming_enable_hwecc; +#endif + +#if USE_DMA + err = nand_dma_init(); + if (err != 0) + return err; +#endif + + /* Figure out the size of the device that we have. + * We need to do this to figure out which ECC + * layout we'll be using. + */ + + err = nand_scan_ident(board_mtd, 1); + if (err) { + printk(KERN_ERR "nand_scan failed: %d\n", err); + iounmap(bcm_umi_io_base); + kfree(board_mtd); + return err; + } + + /* Now that we know the nand size, we can setup the ECC layout */ + + switch (board_mtd->writesize) { /* writesize is the pagesize */ + case 4096: + this->ecc.layout = &nand_hw_eccoob_4096; + break; + case 2048: + this->ecc.layout = &nand_hw_eccoob_2048; + break; + case 512: + this->ecc.layout = &nand_hw_eccoob_512; + break; + default: + { + printk(KERN_ERR "NAND - Unrecognized pagesize: %d\n", + board_mtd->writesize); + return -EINVAL; + } + } + +#if NAND_ECC_BCH + if (board_mtd->writesize > 512) { + if (this->options & NAND_USE_FLASH_BBT) + largepage_bbt.options = NAND_BBT_SCAN2NDPAGE; + this->badblock_pattern = &largepage_bbt; + } +#endif + + /* Now finish off the scan, now that ecc.layout has been initialized. */ + + err = nand_scan_tail(board_mtd); + if (err) { + printk(KERN_ERR "nand_scan failed: %d\n", err); + iounmap(bcm_umi_io_base); + kfree(board_mtd); + return err; + } + + /* Register the partitions */ + { + int nr_partitions; + struct mtd_partition *partition_info; + + board_mtd->name = "bcm_umi-nand"; + nr_partitions = + parse_mtd_partitions(board_mtd, part_probes, + &partition_info, 0); + + if (nr_partitions <= 0) { + printk(KERN_ERR "BCM UMI NAND: Too few partitions - %d\n", + nr_partitions); + iounmap(bcm_umi_io_base); + kfree(board_mtd); + return -EIO; + } + add_mtd_partitions(board_mtd, partition_info, nr_partitions); + } + + /* Return happy */ + return 0; +} + +static int bcm_umi_nand_remove(struct platform_device *pdev) +{ +#if USE_DMA + nand_dma_term(); +#endif + + /* Release resources, unregister device */ + nand_release(board_mtd); + + /* unmap physical adress */ + iounmap(bcm_umi_io_base); + + /* Free the MTD device structure */ + kfree(board_mtd); + + return 0; +} + +#ifdef CONFIG_PM +static int bcm_umi_nand_suspend(struct platform_device *pdev, + pm_message_t state) +{ + printk(KERN_ERR "MTD NAND suspend is being called\n"); + return 0; +} + +static int bcm_umi_nand_resume(struct platform_device *pdev) +{ + printk(KERN_ERR "MTD NAND resume is being called\n"); + return 0; +} +#else +#define bcm_umi_nand_suspend NULL +#define bcm_umi_nand_resume NULL +#endif + +static struct platform_driver nand_driver = { + .driver = { + .name = "bcm-nand", + .owner = THIS_MODULE, + }, + .probe = bcm_umi_nand_probe, + .remove = bcm_umi_nand_remove, + .suspend = bcm_umi_nand_suspend, + .resume = bcm_umi_nand_resume, +}; + +static int __init nand_init(void) +{ + return platform_driver_register(&nand_driver); +} + +static void __exit nand_exit(void) +{ + platform_driver_unregister(&nand_driver); +} + +module_init(nand_init); +module_exit(nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("BCM UMI MTD NAND driver"); diff --git a/drivers/mtd/nand/nand_bcm_umi.c b/drivers/mtd/nand/nand_bcm_umi.c new file mode 100644 index 000000000000..46a6bc9c4b74 --- /dev/null +++ b/drivers/mtd/nand/nand_bcm_umi.c @@ -0,0 +1,149 @@ +/***************************************************************************** +* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ +#include +#include "nand_bcm_umi.h" +#ifdef BOOT0_BUILD +#include +#endif + +/* ---- External Variable Declarations ----------------------------------- */ +/* ---- External Function Prototypes ------------------------------------- */ +/* ---- Public Variables ------------------------------------------------- */ +/* ---- Private Constants and Types -------------------------------------- */ +/* ---- Private Function Prototypes -------------------------------------- */ +/* ---- Private Variables ------------------------------------------------ */ +/* ---- Private Functions ------------------------------------------------ */ + +#if NAND_ECC_BCH +/**************************************************************************** +* nand_bch_ecc_flip_bit - Routine to flip an errored bit +* +* PURPOSE: +* This is a helper routine that flips the bit (0 -> 1 or 1 -> 0) of the +* errored bit specified +* +* PARAMETERS: +* datap - Container that holds the 512 byte data +* errorLocation - Location of the bit that needs to be flipped +* +* RETURNS: +* None +****************************************************************************/ +static void nand_bcm_umi_bch_ecc_flip_bit(uint8_t *datap, int errorLocation) +{ + int locWithinAByte = (errorLocation & REG_UMI_BCH_ERR_LOC_BYTE) >> 0; + int locWithinAWord = (errorLocation & REG_UMI_BCH_ERR_LOC_WORD) >> 3; + int locWithinAPage = (errorLocation & REG_UMI_BCH_ERR_LOC_PAGE) >> 5; + + uint8_t errorByte = 0; + uint8_t byteMask = 1 << locWithinAByte; + + /* BCH uses big endian, need to change the location + * bits to little endian */ + locWithinAWord = 3 - locWithinAWord; + + errorByte = datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord]; + +#ifdef BOOT0_BUILD + puthexs("\nECC Correct Offset: ", + locWithinAPage * sizeof(uint32_t) + locWithinAWord); + puthexs(" errorByte:", errorByte); + puthex8(" Bit: ", locWithinAByte); +#endif + + if (errorByte & byteMask) { + /* bit needs to be cleared */ + errorByte &= ~byteMask; + } else { + /* bit needs to be set */ + errorByte |= byteMask; + } + + /* write back the value with the fixed bit */ + datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord] = errorByte; +} + +/**************************************************************************** +* nand_correct_page_bch - Routine to correct bit errors when reading NAND +* +* PURPOSE: +* This routine reads the BCH registers to determine if there are any bit +* errors during the read of the last 512 bytes of data + ECC bytes. If +* errors exists, the routine fixes it. +* +* PARAMETERS: +* datap - Container that holds the 512 byte data +* +* RETURNS: +* 0 or greater = Number of errors corrected +* (No errors are found or errors have been fixed) +* -1 = Error(s) cannot be fixed +****************************************************************************/ +int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData, + int numEccBytes) +{ + int numErrors; + int errorLocation; + int idx; + uint32_t regValue; + + /* wait for read ECC to be valid */ + regValue = nand_bcm_umi_bch_poll_read_ecc_calc(); + + /* + * read the control status register to determine if there + * are error'ed bits + * see if errors are correctible + */ + if ((regValue & REG_UMI_BCH_CTRL_STATUS_UNCORR_ERR) > 0) { + int i; + + for (i = 0; i < numEccBytes; i++) { + if (readEccData[i] != 0xff) { + /* errors cannot be fixed, return -1 */ + return -1; + } + } + /* If ECC is unprogrammed then we can't correct, + * assume everything OK */ + return 0; + } + + if ((regValue & REG_UMI_BCH_CTRL_STATUS_CORR_ERR) == 0) { + /* no errors */ + return 0; + } + + /* + * Fix errored bits by doing the following: + * 1. Read the number of errors in the control and status register + * 2. Read the error location registers that corresponds to the number + * of errors reported + * 3. Invert the bit in the data + */ + numErrors = (regValue & REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR) >> 20; + + for (idx = 0; idx < numErrors; idx++) { + errorLocation = + REG_UMI_BCH_ERR_LOC_ADDR(idx) & REG_UMI_BCH_ERR_LOC_MASK; + + /* Flip bit */ + nand_bcm_umi_bch_ecc_flip_bit(datap, errorLocation); + } + /* Errors corrected */ + return numErrors; +} +#endif diff --git a/drivers/mtd/nand/nand_bcm_umi.h b/drivers/mtd/nand/nand_bcm_umi.h new file mode 100644 index 000000000000..7cec2cd97854 --- /dev/null +++ b/drivers/mtd/nand/nand_bcm_umi.h @@ -0,0 +1,358 @@ +/***************************************************************************** +* Copyright 2003 - 2009 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ +#ifndef NAND_BCM_UMI_H +#define NAND_BCM_UMI_H + +/* ---- Include Files ---------------------------------------------------- */ +#include +#include +#include + +/* ---- Constants and Types ---------------------------------------------- */ +#if (CFG_GLOBAL_CHIP_FAMILY == CFG_GLOBAL_CHIP_FAMILY_BCMRING) +#define NAND_ECC_BCH (CFG_GLOBAL_CHIP_REV > 0xA0) +#else +#define NAND_ECC_BCH 0 +#endif + +#define CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES 13 + +#if NAND_ECC_BCH +#ifdef BOOT0_BUILD +#define NAND_ECC_NUM_BYTES 13 +#else +#define NAND_ECC_NUM_BYTES CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES +#endif +#else +#define NAND_ECC_NUM_BYTES 3 +#endif + +#define NAND_DATA_ACCESS_SIZE 512 + +/* ---- Variable Externs ------------------------------------------ */ +/* ---- Function Prototypes --------------------------------------- */ +int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData, + int numEccBytes); + +/* Check in device is ready */ +static inline int nand_bcm_umi_dev_ready(void) +{ + return REG_UMI_NAND_RCSR & REG_UMI_NAND_RCSR_RDY; +} + +/* Wait until device is ready */ +static inline void nand_bcm_umi_wait_till_ready(void) +{ + while (nand_bcm_umi_dev_ready() == 0) + ; +} + +/* Enable Hamming ECC */ +static inline void nand_bcm_umi_hamming_enable_hwecc(void) +{ + /* disable and reset ECC, 512 byte page */ + REG_UMI_NAND_ECC_CSR &= ~(REG_UMI_NAND_ECC_CSR_ECC_ENABLE | + REG_UMI_NAND_ECC_CSR_256BYTE); + /* enable ECC */ + REG_UMI_NAND_ECC_CSR |= REG_UMI_NAND_ECC_CSR_ECC_ENABLE; +} + +#if NAND_ECC_BCH +/* BCH ECC specifics */ +#define ECC_BITS_PER_CORRECTABLE_BIT 13 + +/* Enable BCH Read ECC */ +static inline void nand_bcm_umi_bch_enable_read_hwecc(void) +{ + /* disable and reset ECC */ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID; + /* Turn on ECC */ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN; +} + +/* Enable BCH Write ECC */ +static inline void nand_bcm_umi_bch_enable_write_hwecc(void) +{ + /* disable and reset ECC */ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID; + /* Turn on ECC */ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN; +} + +/* Config number of BCH ECC bytes */ +static inline void nand_bcm_umi_bch_config_ecc(uint8_t numEccBytes) +{ + uint32_t nValue; + uint32_t tValue; + uint32_t kValue; + uint32_t numBits = numEccBytes * 8; + + /* disable and reset ECC */ + REG_UMI_BCH_CTRL_STATUS = + REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID | + REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID; + + /* Every correctible bit requires 13 ECC bits */ + tValue = (uint32_t) (numBits / ECC_BITS_PER_CORRECTABLE_BIT); + + /* Total data in number of bits for generating and computing BCH ECC */ + nValue = (NAND_DATA_ACCESS_SIZE + numEccBytes) * 8; + + /* K parameter is used internally. K = N - (T * 13) */ + kValue = nValue - (tValue * ECC_BITS_PER_CORRECTABLE_BIT); + + /* Write the settings */ + REG_UMI_BCH_N = nValue; + REG_UMI_BCH_T = tValue; + REG_UMI_BCH_K = kValue; +} + +/* Pause during ECC read calculation to skip bytes in OOB */ +static inline void nand_bcm_umi_bch_pause_read_ecc_calc(void) +{ + REG_UMI_BCH_CTRL_STATUS = + REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN | + REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC; +} + +/* Resume during ECC read calculation after skipping bytes in OOB */ +static inline void nand_bcm_umi_bch_resume_read_ecc_calc(void) +{ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN; +} + +/* Poll read ECC calc to check when hardware completes */ +static inline uint32_t nand_bcm_umi_bch_poll_read_ecc_calc(void) +{ + uint32_t regVal; + + do { + /* wait for ECC to be valid */ + regVal = REG_UMI_BCH_CTRL_STATUS; + } while ((regVal & REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID) == 0); + + return regVal; +} + +/* Poll write ECC calc to check when hardware completes */ +static inline void nand_bcm_umi_bch_poll_write_ecc_calc(void) +{ + /* wait for ECC to be valid */ + while ((REG_UMI_BCH_CTRL_STATUS & REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID) + == 0) + ; +} + +/* Read the OOB and ECC, for kernel write OOB to a buffer */ +#if defined(__KERNEL__) && !defined(STANDALONE) +static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, + uint8_t *eccCalc, int numEccBytes, uint8_t *oobp) +#else +static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, + uint8_t *eccCalc, int numEccBytes) +#endif +{ + int eccPos = 0; + int numToRead = 16; /* There are 16 bytes per sector in the OOB */ + + /* ECC is already paused when this function is called */ + + if (pageSize == NAND_DATA_ACCESS_SIZE) { + while (numToRead > numEccBytes) { + /* skip free oob region */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + } + + /* read ECC bytes before BI */ + nand_bcm_umi_bch_resume_read_ecc_calc(); + + while (numToRead > 11) { +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp = REG_NAND_DATA8; + eccCalc[eccPos++] = *oobp; + oobp++; +#else + eccCalc[eccPos++] = REG_NAND_DATA8; +#endif + } + + nand_bcm_umi_bch_pause_read_ecc_calc(); + + if (numToRead == 11) { + /* read BI */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + } + + /* read ECC bytes */ + nand_bcm_umi_bch_resume_read_ecc_calc(); + while (numToRead) { +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp = REG_NAND_DATA8; + eccCalc[eccPos++] = *oobp; + oobp++; +#else + eccCalc[eccPos++] = REG_NAND_DATA8; +#endif + numToRead--; + } + } else { + /* skip BI */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + + while (numToRead > numEccBytes) { + /* skip free oob region */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + } + + /* read ECC bytes */ + nand_bcm_umi_bch_resume_read_ecc_calc(); + while (numToRead) { +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp = REG_NAND_DATA8; + eccCalc[eccPos++] = *oobp; + oobp++; +#else + eccCalc[eccPos++] = REG_NAND_DATA8; +#endif + numToRead--; + } + } +} + +/* Helper function to write ECC */ +static inline void NAND_BCM_UMI_ECC_WRITE(int numEccBytes, int eccBytePos, + uint8_t *oobp, uint8_t eccVal) +{ + if (eccBytePos <= numEccBytes) + *oobp = eccVal; +} + +/* Write OOB with ECC */ +static inline void nand_bcm_umi_bch_write_oobEcc(uint32_t pageSize, + uint8_t *oobp, int numEccBytes) +{ + uint32_t eccVal = 0xffffffff; + + /* wait for write ECC to be valid */ + nand_bcm_umi_bch_poll_write_ecc_calc(); + + /* + ** Get the hardware ecc from the 32-bit result registers. + ** Read after 512 byte accesses. Format B3B2B1B0 + ** where B3 = ecc3, etc. + */ + + if (pageSize == NAND_DATA_ACCESS_SIZE) { + /* Now fill in the ECC bytes */ + if (numEccBytes >= 13) + eccVal = REG_UMI_BCH_WR_ECC_3; + + /* Usually we skip CM in oob[0,1] */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[0], + (eccVal >> 16) & 0xff); + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[1], + (eccVal >> 8) & 0xff); + + /* Write ECC in oob[2,3,4] */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[2], + eccVal & 0xff); /* ECC 12 */ + + if (numEccBytes >= 9) + eccVal = REG_UMI_BCH_WR_ECC_2; + + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[3], + (eccVal >> 24) & 0xff); /* ECC11 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[4], + (eccVal >> 16) & 0xff); /* ECC10 */ + + /* Always Skip BI in oob[5] */ + } else { + /* Always Skip BI in oob[0] */ + + /* Now fill in the ECC bytes */ + if (numEccBytes >= 13) + eccVal = REG_UMI_BCH_WR_ECC_3; + + /* Usually skip CM in oob[1,2] */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[1], + (eccVal >> 16) & 0xff); + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[2], + (eccVal >> 8) & 0xff); + + /* Write ECC in oob[3-15] */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[3], + eccVal & 0xff); /* ECC12 */ + + if (numEccBytes >= 9) + eccVal = REG_UMI_BCH_WR_ECC_2; + + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[4], + (eccVal >> 24) & 0xff); /* ECC11 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[5], + (eccVal >> 16) & 0xff); /* ECC10 */ + } + + /* Fill in the remainder of ECC locations */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 10, &oobp[6], + (eccVal >> 8) & 0xff); /* ECC9 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 9, &oobp[7], + eccVal & 0xff); /* ECC8 */ + + if (numEccBytes >= 5) + eccVal = REG_UMI_BCH_WR_ECC_1; + + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 8, &oobp[8], + (eccVal >> 24) & 0xff); /* ECC7 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 7, &oobp[9], + (eccVal >> 16) & 0xff); /* ECC6 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 6, &oobp[10], + (eccVal >> 8) & 0xff); /* ECC5 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 5, &oobp[11], + eccVal & 0xff); /* ECC4 */ + + if (numEccBytes >= 1) + eccVal = REG_UMI_BCH_WR_ECC_0; + + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 4, &oobp[12], + (eccVal >> 24) & 0xff); /* ECC3 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 3, &oobp[13], + (eccVal >> 16) & 0xff); /* ECC2 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 2, &oobp[14], + (eccVal >> 8) & 0xff); /* ECC1 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 1, &oobp[15], + eccVal & 0xff); /* ECC0 */ +} +#endif + +#endif /* NAND_BCM_UMI_H */ -- cgit v1.2.3 From 1cc523999e0acd0c21f83bf89b7a44b4ab6f7bd8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 20 Oct 2009 22:02:41 +0900 Subject: Revert "mtd: mxc_nand: fix 2KiB pagesize NAND on i.MX27" This reverts commit 71b7d0d90d536ae4e70929cc59a1a9f6ba457c6c. The problem which that commit attempted to fix was a bootloader issue, which had been misunderstood. The 'fix' causes lots of false bad blocks for existing users with sane firmware. Thanks to Mathieu Berland for diagnosing the problem coherently. Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index ef3e1998b591..06c531485df9 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -857,17 +857,6 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, } } -/* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks. */ -static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; - -static struct nand_bbt_descr smallpage_memorybased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = 5, - .len = 1, - .pattern = scan_ff_pattern -}; - static int __init mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; @@ -984,10 +973,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } - if (mtd->writesize == 2048) { - host->pagesize_2k = 1; - this->badblock_pattern = &smallpage_memorybased; - } + host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; if (this->ecc.mode == NAND_ECC_HW) { switch (mtd->oobsize) { -- cgit v1.2.3 From 8c1fd89a85f898384df02217c09c98c2f39b4832 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 10:22:01 +0200 Subject: mxc_nand: cleanup eccoob descriptions The original Freescale driver used to have eccoob descriptions like this: static struct nand_ecclayout nand_hw_eccoob_8 = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, .oobfree = {{0, 5}, {11, 5}} }; static struct nand_ecclayout nand_hw_eccoob_16 = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, .oobfree = {{0, 6}, {12, 4}} }; The former was used for 8bit flashes and the latter for 16bit flashes. They honored the fact that the bad block marker on 8bit flashes is on byte 5 while on 16bit flashes it is on byte 11. In the Kernel driver this was copied wrong and we ended up with two identical descriptions. Change it so that we have only one description which leaves byte 5 and byte 11 unspecified so that it won't be used by others. Also, rename the descriptions to nand_hw_eccoob_smallpage and nand_hw_eccoob_largepage so that it can't be confused with Nand chip bus widths (what actually happened in this driver) Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 06c531485df9..2aae5a7ddbd1 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -129,19 +129,13 @@ struct mxc_nand_host { #define SPARE_SINGLEBIT_ERROR 0x1 /* OOB placement block for use with hardware ecc generation */ -static struct nand_ecclayout nand_hw_eccoob_8 = { +static struct nand_ecclayout nand_hw_eccoob_smallpage = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, - .oobfree = {{0, 5}, {11, 5}, } + .oobfree = {{0, 5}, {12, 4}, } }; -static struct nand_ecclayout nand_hw_eccoob_16 = { - .eccbytes = 5, - .eccpos = {6, 7, 8, 9, 10}, - .oobfree = {{0, 5}, {11, 5}, } -}; - -static struct nand_ecclayout nand_hw_eccoob_64 = { +static struct nand_ecclayout nand_hw_eccoob_largepage = { .eccbytes = 20, .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, @@ -940,7 +934,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) } else { this->ecc.size = 512; this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_8; + this->ecc.layout = &nand_hw_eccoob_smallpage; this->ecc.mode = NAND_ECC_SOFT; tmp = readw(host->regs + NFC_CONFIG1); tmp &= ~NFC_ECC_EN; @@ -964,7 +958,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) /* NAND bus width determines access funtions used by upper layer */ if (pdata->width == 2) { this->options |= NAND_BUSWIDTH_16; - this->ecc.layout = &nand_hw_eccoob_16; + this->ecc.layout = &nand_hw_eccoob_smallpage; } /* first scan to find the device and get the page size */ @@ -978,20 +972,20 @@ static int __init mxcnd_probe(struct platform_device *pdev) if (this->ecc.mode == NAND_ECC_HW) { switch (mtd->oobsize) { case 8: - this->ecc.layout = &nand_hw_eccoob_8; + this->ecc.layout = &nand_hw_eccoob_smallpage; break; case 16: - this->ecc.layout = &nand_hw_eccoob_16; + this->ecc.layout = &nand_hw_eccoob_smallpage; break; case 64: - this->ecc.layout = &nand_hw_eccoob_64; + this->ecc.layout = &nand_hw_eccoob_largepage; break; default: /* page size not handled by HW ECC */ /* switching back to soft ECC */ this->ecc.size = 512; this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_8; + this->ecc.layout = &nand_hw_eccoob_smallpage; this->ecc.mode = NAND_ECC_SOFT; this->ecc.calculate = NULL; this->ecc.correct = NULL; -- cgit v1.2.3 From 13e1add1073f55d5361e5357016b631efc03bff8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 10:39:05 +0200 Subject: mxc_nand: cleanup initialization The oob layout was initialized several times. Instead, use a smallpage layout by default and switch to a largepage afterwards if necessary. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 72 ++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 50 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 2aae5a7ddbd1..dafa1f0e04db 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -921,45 +921,42 @@ static int __init mxcnd_probe(struct platform_device *pdev) if (err) goto eirq; + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* preset operation */ + /* Unlock the internal RAM Buffer */ + writew(0x2, host->regs + NFC_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, host->regs + NFC_UNLOCKSTART_BLKADDR); + writew(0x4000, host->regs + NFC_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + writew(0x4, host->regs + NFC_WRPROT); + + this->ecc.size = 512; + this->ecc.bytes = 3; + this->ecc.layout = &nand_hw_eccoob_smallpage; + if (pdata->hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = mxc_nand_correct_data; this->ecc.mode = NAND_ECC_HW; - this->ecc.size = 512; - this->ecc.bytes = 3; tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_ECC_EN; writew(tmp, host->regs + NFC_CONFIG1); } else { - this->ecc.size = 512; - this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_smallpage; this->ecc.mode = NAND_ECC_SOFT; tmp = readw(host->regs + NFC_CONFIG1); tmp &= ~NFC_ECC_EN; writew(tmp, host->regs + NFC_CONFIG1); } - /* Reset NAND */ - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* preset operation */ - /* Unlock the internal RAM Buffer */ - writew(0x2, host->regs + NFC_CONFIG); - - /* Blocks to be unlocked */ - writew(0x0, host->regs + NFC_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_UNLOCKEND_BLKADDR); - - /* Unlock Block Command for given address range */ - writew(0x4, host->regs + NFC_WRPROT); - /* NAND bus width determines access funtions used by upper layer */ - if (pdata->width == 2) { + if (pdata->width == 2) this->options |= NAND_BUSWIDTH_16; - this->ecc.layout = &nand_hw_eccoob_smallpage; - } /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1)) { @@ -967,34 +964,9 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } - host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; - - if (this->ecc.mode == NAND_ECC_HW) { - switch (mtd->oobsize) { - case 8: - this->ecc.layout = &nand_hw_eccoob_smallpage; - break; - case 16: - this->ecc.layout = &nand_hw_eccoob_smallpage; - break; - case 64: - this->ecc.layout = &nand_hw_eccoob_largepage; - break; - default: - /* page size not handled by HW ECC */ - /* switching back to soft ECC */ - this->ecc.size = 512; - this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_smallpage; - this->ecc.mode = NAND_ECC_SOFT; - this->ecc.calculate = NULL; - this->ecc.correct = NULL; - this->ecc.hwctl = NULL; - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_ECC_EN; - writew(tmp, host->regs + NFC_CONFIG1); - break; - } + if (mtd->writesize == 2048) { + host->pagesize_2k = 1; + this->ecc.layout = &nand_hw_eccoob_largepage; } /* second phase scan */ -- cgit v1.2.3 From 06ecb04ac5c038248d2bcee92a2a4259f2acfa31 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 2 Jun 2009 11:37:53 +0200 Subject: mxc_nand: merge send_read_page and send_prog_page Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 54 +++++++++++---------------------------------- 1 file changed, 13 insertions(+), 41 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index dafa1f0e04db..8aa8a429e6b8 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -226,12 +226,10 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) wait_op_done(host, TROP_US_DELAY, addr, islast); } -/* This function requests the NANDFC to initate the transfer - * of data currently in the NANDFC RAM buffer to the NAND device. */ -static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only) +static void send_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only, unsigned int ops) { - DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", spare_only); + DEBUG(MTD_DEBUG_LEVEL3, "send_page (%d)\n", spare_only); /* NANDFC buffer 0 is used for page read/write */ writew(buf_id, host->regs + NFC_BUF_ADDR); @@ -246,33 +244,7 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, writew(config1, host->regs + NFC_CONFIG1); } - writew(NFC_INPUT, host->regs + NFC_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, spare_only, true); -} - -/* Requests NANDFC to initated the transfer of data from the - * NAND device into in the NANDFC ram buffer. */ -static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only) -{ - DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); - - /* NANDFC buffer 0 is used for page read/write */ - writew(buf_id, host->regs + NFC_BUF_ADDR); - - /* Configure spare or page+spare access */ - if (!host->pagesize_2k) { - uint32_t config1 = readw(host->regs + NFC_CONFIG1); - if (spare_only) - config1 |= NFC_SP_EN; - else - config1 &= ~NFC_SP_EN; - writew(config1, host->regs + NFC_CONFIG1); - } - - writew(NFC_OUTPUT, host->regs + NFC_CONFIG2); + writew(ops, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, spare_only, true); @@ -756,13 +728,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_PAGEPROG: - send_prog_page(host, 0, host->spare_only); + send_page(host, 0, host->spare_only, NFC_INPUT); if (host->pagesize_2k) { /* data in 4 areas datas */ - send_prog_page(host, 1, host->spare_only); - send_prog_page(host, 2, host->spare_only); - send_prog_page(host, 3, host->spare_only); + send_page(host, 1, host->spare_only, NFC_INPUT); + send_page(host, 2, host->spare_only, NFC_INPUT); + send_page(host, 3, host->spare_only, NFC_INPUT); } break; @@ -827,12 +799,12 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* send read confirm command */ send_cmd(host, NAND_CMD_READSTART, true); /* read for each AREA */ - send_read_page(host, 0, host->spare_only); - send_read_page(host, 1, host->spare_only); - send_read_page(host, 2, host->spare_only); - send_read_page(host, 3, host->spare_only); + send_page(host, 0, host->spare_only, NFC_OUTPUT); + send_page(host, 1, host->spare_only, NFC_OUTPUT); + send_page(host, 2, host->spare_only, NFC_OUTPUT); + send_page(host, 3, host->spare_only, NFC_OUTPUT); } else - send_read_page(host, 0, host->spare_only); + send_page(host, 0, host->spare_only, NFC_OUTPUT); break; case NAND_CMD_READID: -- cgit v1.2.3 From a3e65b64d5067f86b929eabde82f132b81437da8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 2 Jun 2009 11:47:59 +0200 Subject: mxc_nand: introduce mxc_do_addr_cycle This factors the address cycle to a seperate function. This becomes useful in a later patch where we can simplify the command processing by making use of this function. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 87 ++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 40 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 8aa8a429e6b8..325645c60488 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -658,6 +658,52 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) } } +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + /* Write out column address, if necessary */ + if (column != -1) { + /* + * MXC NANDFC can only perform full page+spare or + * spare-only read/write. When the upper layers + * layers perform a read/write buf operation, + * we will used the saved column adress to index into + * the full page. + */ + send_addr(host, 0, page_addr == -1); + if (host->pagesize_2k) + /* another col addr cycle for 2k page */ + send_addr(host, 0, false); + } + + /* Write out page address, if necessary */ + if (page_addr != -1) { + /* paddr_0 - p_addr_7 */ + send_addr(host, (page_addr & 0xff), false); + + if (host->pagesize_2k) { + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, false); + send_addr(host, (page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, true); + } else { + /* One more address cycle for higher density devices */ + if (mtd->size >= 0x4000000) { + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, false); + send_addr(host, (page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, true); + } + } +} + /* Used by the upper layer to write command to NAND Flash for * different operations to be carried out on NAND Flash */ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, @@ -746,46 +792,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Write out the command to the device. */ send_cmd(host, command, useirq); - - /* Write out column address, if necessary */ - if (column != -1) { - /* - * MXC NANDFC can only perform full page+spare or - * spare-only read/write. When the upper layers - * layers perform a read/write buf operation, - * we will used the saved column adress to index into - * the full page. - */ - send_addr(host, 0, page_addr == -1); - if (host->pagesize_2k) - /* another col addr cycle for 2k page */ - send_addr(host, 0, false); - } - - /* Write out page address, if necessary */ - if (page_addr != -1) { - /* paddr_0 - p_addr_7 */ - send_addr(host, (page_addr & 0xff), false); - - if (host->pagesize_2k) { - if (mtd->size >= 0x10000000) { - /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, false); - send_addr(host, (page_addr >> 16) & 0xff, true); - } else - /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, true); - } else { - /* One more address cycle for higher density devices */ - if (mtd->size >= 0x4000000) { - /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, false); - send_addr(host, (page_addr >> 16) & 0xff, true); - } else - /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, true); - } - } + mxc_do_addr_cycle(mtd, column, page_addr); /* Command post-processing step */ switch (command) { -- cgit v1.2.3 From 624654917476c63287429e005173aa8629c92eac Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 15:57:20 +0200 Subject: mxc nand: remove debug param Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 325645c60488..f981a1f054d6 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -165,7 +165,7 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) * complete by checking the INT bit of config2 register. */ static void wait_op_done(struct mxc_nand_host *host, int max_retries, - uint16_t param, int useirq) + int useirq) { uint32_t tmp; @@ -194,8 +194,8 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries, udelay(1); } if (max_retries < 0) - DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", - __func__, param); + DEBUG(MTD_DEBUG_LEVEL0, "%s: INT not set\n", + __func__); } } @@ -209,7 +209,7 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(NFC_CMD, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, cmd, useirq); + wait_op_done(host, TROP_US_DELAY, useirq); } /* This function sends an address (or partial address) to the @@ -223,7 +223,7 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) writew(NFC_ADDR, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, addr, islast); + wait_op_done(host, TROP_US_DELAY, islast); } static void send_page(struct mxc_nand_host *host, uint8_t buf_id, @@ -247,7 +247,7 @@ static void send_page(struct mxc_nand_host *host, uint8_t buf_id, writew(ops, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, spare_only, true); + wait_op_done(host, TROP_US_DELAY, true); } /* Request the NANDFC to perform a read of the NAND device ID. */ @@ -267,7 +267,7 @@ static void send_read_id(struct mxc_nand_host *host) writew(NFC_ID, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, 0, true); + wait_op_done(host, TROP_US_DELAY, true); if (this->options & NAND_BUSWIDTH_16) { void __iomem *main_buf = host->regs + MAIN_AREA0; @@ -303,7 +303,7 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) writew(NFC_STATUS, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, 0, true); + wait_op_done(host, TROP_US_DELAY, true); /* Status is placed in first word of main buffer */ /* get status, then recovery area 1 data */ -- cgit v1.2.3 From a4ad57f8b320c9d89b7693be82e47347425db918 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 16:12:40 +0200 Subject: mxc nand: remove dead code Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index f981a1f054d6..1ffb854f989e 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -620,23 +620,6 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; -#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE - if (chip > 0) { - DEBUG(MTD_DEBUG_LEVEL0, - "ERROR: Illegal chip select (chip = %d)\n", chip); - return; - } - - if (chip == -1) { - writew(readw(host->regs + NFC_CONFIG1) & ~NFC_CE, - host->regs + NFC_CONFIG1); - return; - } - - writew(readw(host->regs + NFC_CONFIG1) | NFC_CE, - host->regs + NFC_CONFIG1); -#endif - switch (chip) { case -1: /* Disable the NFC clock */ -- cgit v1.2.3 From d970a0730ba5598b711d42bac287a17db5b6268a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 16:16:01 +0200 Subject: mxc nand: use resource_size() Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 1ffb854f989e..8fcb33da8efc 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -865,7 +865,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto eres; } - host->regs = ioremap(res->start, res->end - res->start + 1); + host->regs = ioremap(res->start, resource_size(res)); if (!host->regs) { err = -ENOMEM; goto eres; -- cgit v1.2.3 From f8f9608d9b7a5b3349605284a4f3876e898413f1 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 17:12:26 +0200 Subject: mxc nand: use buffers The NAND controller has some limitations how to access the internal buffers. It only allows 32 bit accesses. The driver used to work around this by having special alignment aware copy routines. We now copy the whole page to a buffer in memory and let the access functions use this buffer. This simplifies the driver. A bonnie++ test showed that this has no negative performance impact on the driver. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 310 +++++++++++--------------------------------- 1 file changed, 77 insertions(+), 233 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 8fcb33da8efc..03d20086170b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -107,15 +107,17 @@ struct mxc_nand_host { struct device *dev; void __iomem *regs; - int spare_only; int status_request; int pagesize_2k; - uint16_t col_addr; struct clk *clk; int clk_act; int irq; wait_queue_head_t irq_waitq; + + uint8_t *data_buf; + unsigned int buf_start; + int spare_len; }; /* Define delays in microsec for NAND device operations */ @@ -227,23 +229,11 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) } static void send_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only, unsigned int ops) + unsigned int ops) { - DEBUG(MTD_DEBUG_LEVEL3, "send_page (%d)\n", spare_only); - /* NANDFC buffer 0 is used for page read/write */ writew(buf_id, host->regs + NFC_BUF_ADDR); - /* Configure spare or page+spare access */ - if (!host->pagesize_2k) { - uint16_t config1 = readw(host->regs + NFC_CONFIG1); - if (spare_only) - config1 |= NFC_SP_EN; - else - config1 &= ~(NFC_SP_EN); - writew(config1, host->regs + NFC_CONFIG1); - } - writew(ops, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ @@ -278,6 +268,7 @@ static void send_read_id(struct mxc_nand_host *host) writeb(readb(main_buf + 8), main_buf + 4); writeb(readb(main_buf + 10), main_buf + 5); } + memcpy(host->data_buf, host->regs + MAIN_AREA0, 16); } /* This function requests the NANDFC to perform a read of the @@ -363,32 +354,14 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - uint8_t ret = 0; - uint16_t col, rd_word; - uint16_t __iomem *main_buf = host->regs + MAIN_AREA0; - uint16_t __iomem *spare_buf = host->regs + SPARE_AREA0; + uint8_t ret; /* Check for status request */ if (host->status_request) return get_dev_status(host) & 0xFF; - /* Get column for 16-bit access */ - col = host->col_addr >> 1; - - /* If we are accessing the spare region */ - if (host->spare_only) - rd_word = readw(&spare_buf[col]); - else - rd_word = readw(&main_buf[col]); - - /* Pick upper/lower byte of word from RAM buffer */ - if (host->col_addr & 0x1) - ret = (rd_word >> 8) & 0xFF; - else - ret = rd_word & 0xFF; - - /* Update saved column address */ - host->col_addr++; + ret = *(uint8_t *)(host->data_buf + host->buf_start); + host->buf_start++; return ret; } @@ -397,33 +370,10 @@ static uint16_t mxc_nand_read_word(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - uint16_t col, rd_word, ret; - uint16_t __iomem *p; + uint16_t ret; - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_read_word(col = %d)\n", host->col_addr); - - col = host->col_addr; - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - if (col < mtd->writesize) - p = (host->regs + MAIN_AREA0) + (col >> 1); - else - p = (host->regs + SPARE_AREA0) + ((col - mtd->writesize) >> 1); - - if (col & 1) { - rd_word = readw(p); - ret = (rd_word >> 8) & 0xff; - rd_word = readw(&p[1]); - ret |= (rd_word << 8) & 0xff00; - - } else - ret = readw(p); - - /* Update saved column address */ - host->col_addr = col + 2; + ret = *(uint16_t *)(host->data_buf + host->buf_start); + host->buf_start += 2; return ret; } @@ -436,94 +386,14 @@ static void mxc_nand_write_buf(struct mtd_info *mtd, { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - int n, col, i = 0; - - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, - len); - - col = host->col_addr; - - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - n = mtd->writesize + mtd->oobsize - col; - n = min(len, n); - - DEBUG(MTD_DEBUG_LEVEL3, - "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); - - while (n) { - void __iomem *p; - - if (col < mtd->writesize) - p = host->regs + MAIN_AREA0 + (col & ~3); - else - p = host->regs + SPARE_AREA0 - - mtd->writesize + (col & ~3); - - DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__, - __LINE__, p); - - if (((col | (int)&buf[i]) & 3) || n < 16) { - uint32_t data = 0; - - if (col & 3 || n < 4) - data = readl(p); - - switch (col & 3) { - case 0: - if (n) { - data = (data & 0xffffff00) | - (buf[i++] << 0); - n--; - col++; - } - case 1: - if (n) { - data = (data & 0xffff00ff) | - (buf[i++] << 8); - n--; - col++; - } - case 2: - if (n) { - data = (data & 0xff00ffff) | - (buf[i++] << 16); - n--; - col++; - } - case 3: - if (n) { - data = (data & 0x00ffffff) | - (buf[i++] << 24); - n--; - col++; - } - } - - writel(data, p); - } else { - int m = mtd->writesize - col; + u16 col = host->buf_start; + int n = mtd->oobsize + mtd->writesize - col; - if (col >= mtd->writesize) - m += mtd->oobsize; + n = min(n, len); - m = min(n, m) & ~3; + memcpy(host->data_buf + col, buf, n); - DEBUG(MTD_DEBUG_LEVEL3, - "%s:%d: n = %d, m = %d, i = %d, col = %d\n", - __func__, __LINE__, n, m, i, col); - - memcpy(p, &buf[i], m); - col += m; - i += m; - n -= m; - } - } - /* Update saved column address */ - host->col_addr = col; + host->buf_start += n; } /* Read the data buffer from the NAND Flash. To read the data from NAND @@ -534,75 +404,14 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - int n, col, i = 0; + u16 col = host->buf_start; + int n = mtd->oobsize + mtd->writesize - col; - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, len); - - col = host->col_addr; - - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - n = mtd->writesize + mtd->oobsize - col; - n = min(len, n); - - while (n) { - void __iomem *p; - - if (col < mtd->writesize) - p = host->regs + MAIN_AREA0 + (col & ~3); - else - p = host->regs + SPARE_AREA0 - - mtd->writesize + (col & ~3); - - if (((col | (int)&buf[i]) & 3) || n < 16) { - uint32_t data; - - data = readl(p); - switch (col & 3) { - case 0: - if (n) { - buf[i++] = (uint8_t) (data); - n--; - col++; - } - case 1: - if (n) { - buf[i++] = (uint8_t) (data >> 8); - n--; - col++; - } - case 2: - if (n) { - buf[i++] = (uint8_t) (data >> 16); - n--; - col++; - } - case 3: - if (n) { - buf[i++] = (uint8_t) (data >> 24); - n--; - col++; - } - } - } else { - int m = mtd->writesize - col; + n = min(n, len); - if (col >= mtd->writesize) - m += mtd->oobsize; - - m = min(n, m) & ~3; - memcpy(&buf[i], p, m); - col += m; - i += m; - n -= m; - } - } - /* Update saved column address */ - host->col_addr = col; + memcpy(buf, host->data_buf + col, len); + host->buf_start += len; } /* Used by the upper layer to verify the data in NAND Flash @@ -641,6 +450,36 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) } } +/* + * Function to transfer data to/from spare area. + */ +static void copy_spare(struct mtd_info *mtd, bool bfrom) +{ + struct nand_chip *this = mtd->priv; + struct mxc_nand_host *host = this->priv; + u16 i, j; + u16 n = mtd->writesize >> 9; + u8 *d = host->data_buf + mtd->writesize; + u8 *s = host->regs + SPARE_AREA0; + u16 t = host->spare_len; + + j = (mtd->oobsize / n >> 1) << 1; + + if (bfrom) { + for (i = 0; i < n - 1; i++) + memcpy(d + i * j, s + i * t, j); + + /* the last section */ + memcpy(d + i * j, s + i * t, mtd->oobsize - i * j); + } else { + for (i = 0; i < n - 1; i++) + memcpy(&s[i * t], &d[i * j], j); + + /* the last section */ + memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j); + } +} + static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) { struct nand_chip *nand_chip = mtd->priv; @@ -707,19 +546,18 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, switch (command) { case NAND_CMD_STATUS: - host->col_addr = 0; + host->buf_start = 0; host->status_request = true; break; case NAND_CMD_READ0: - host->col_addr = column; - host->spare_only = false; + host->buf_start = column; useirq = false; break; case NAND_CMD_READOOB: - host->col_addr = column; - host->spare_only = true; + host->buf_start = column + mtd->writesize; + useirq = false; if (host->pagesize_2k) command = NAND_CMD_READ0; /* only READ0 is valid */ @@ -739,15 +577,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); - host->col_addr = column - mtd->writesize; - host->spare_only = true; + host->buf_start = column; /* Set program pointer to spare region */ if (!host->pagesize_2k) send_cmd(host, NAND_CMD_READOOB, false); } else { - host->spare_only = false; - host->col_addr = column; + host->buf_start = column; /* Set program pointer to page start */ if (!host->pagesize_2k) @@ -757,13 +593,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_PAGEPROG: - send_page(host, 0, host->spare_only, NFC_INPUT); + memcpy(host->regs + MAIN_AREA0, host->data_buf, mtd->writesize); + copy_spare(mtd, false); + send_page(host, 0, NFC_INPUT); if (host->pagesize_2k) { /* data in 4 areas datas */ - send_page(host, 1, host->spare_only, NFC_INPUT); - send_page(host, 2, host->spare_only, NFC_INPUT); - send_page(host, 3, host->spare_only, NFC_INPUT); + send_page(host, 1, NFC_INPUT); + send_page(host, 2, NFC_INPUT); + send_page(host, 3, NFC_INPUT); } break; @@ -789,16 +627,18 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* send read confirm command */ send_cmd(host, NAND_CMD_READSTART, true); /* read for each AREA */ - send_page(host, 0, host->spare_only, NFC_OUTPUT); - send_page(host, 1, host->spare_only, NFC_OUTPUT); - send_page(host, 2, host->spare_only, NFC_OUTPUT); - send_page(host, 3, host->spare_only, NFC_OUTPUT); + send_page(host, 0, NFC_OUTPUT); + send_page(host, 1, NFC_OUTPUT); + send_page(host, 2, NFC_OUTPUT); + send_page(host, 3, NFC_OUTPUT); } else - send_page(host, 0, host->spare_only, NFC_OUTPUT); + send_page(host, 0, NFC_OUTPUT); + + memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); + copy_spare(mtd, true); break; case NAND_CMD_READID: - host->col_addr = 0; send_read_id(host); break; @@ -824,10 +664,14 @@ static int __init mxcnd_probe(struct platform_device *pdev) int err = 0, nr_parts = 0; /* Allocate memory for MTD device structure and private data */ - host = kzalloc(sizeof(struct mxc_nand_host), GFP_KERNEL); + host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + + NAND_MAX_OOBSIZE, GFP_KERNEL); if (!host) return -ENOMEM; + host->data_buf = (uint8_t *)(host + 1); + host->spare_len = 16; + host->dev = &pdev->dev; /* structures must be linked */ this = &host->nand; -- cgit v1.2.3 From 89121a6bfecf4bd81b360fa1b3ae8875ed3e4a71 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 17:18:01 +0200 Subject: mxc nand: simplify command processing Instead of having two switch/case with other operations in between, use only one switch/case Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 81 ++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 46 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 03d20086170b..67ddc35ca7bc 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -533,7 +533,6 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - int useirq = true; DEBUG(MTD_DEBUG_LEVEL3, "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", @@ -548,19 +547,37 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_STATUS: host->buf_start = 0; host->status_request = true; - break; - case NAND_CMD_READ0: - host->buf_start = column; - useirq = false; + send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); break; + case NAND_CMD_READ0: case NAND_CMD_READOOB: - host->buf_start = column + mtd->writesize; + if (command == NAND_CMD_READ0) + host->buf_start = column; + else + host->buf_start = column + mtd->writesize; - useirq = false; if (host->pagesize_2k) command = NAND_CMD_READ0; /* only READ0 is valid */ + + send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); + + if (host->pagesize_2k) { + /* send read confirm command */ + send_cmd(host, NAND_CMD_READSTART, true); + /* read for each AREA */ + send_page(host, 0, NFC_OUTPUT); + send_page(host, 1, NFC_OUTPUT); + send_page(host, 2, NFC_OUTPUT); + send_page(host, 3, NFC_OUTPUT); + } else + send_page(host, 0, NFC_OUTPUT); + + memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); + copy_spare(mtd, true); break; case NAND_CMD_SEQIN: @@ -589,7 +606,9 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, if (!host->pagesize_2k) send_cmd(host, NAND_CMD_READ0, false); } - useirq = false; + + send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_PAGEPROG: @@ -604,51 +623,21 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_page(host, 3, NFC_INPUT); } - break; - - case NAND_CMD_ERASE1: - useirq = false; - break; - } - - /* Write out the command to the device. */ - send_cmd(host, command, useirq); - mxc_do_addr_cycle(mtd, column, page_addr); - - /* Command post-processing step */ - switch (command) { - - case NAND_CMD_RESET: - break; - - case NAND_CMD_READOOB: - case NAND_CMD_READ0: - if (host->pagesize_2k) { - /* send read confirm command */ - send_cmd(host, NAND_CMD_READSTART, true); - /* read for each AREA */ - send_page(host, 0, NFC_OUTPUT); - send_page(host, 1, NFC_OUTPUT); - send_page(host, 2, NFC_OUTPUT); - send_page(host, 3, NFC_OUTPUT); - } else - send_page(host, 0, NFC_OUTPUT); - - memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); - copy_spare(mtd, true); + send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_READID: + send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); send_read_id(host); break; - case NAND_CMD_PAGEPROG: - break; - - case NAND_CMD_STATUS: - break; - + case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: + send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); + break; } } -- cgit v1.2.3 From c5d23f1bf384e4a17bc4a014313a4157c015a820 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 17:25:53 +0200 Subject: mxc nand: modify send_page to send all pages, not only one Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 67ddc35ca7bc..1131ad126fd2 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -228,16 +228,25 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) wait_op_done(host, TROP_US_DELAY, islast); } -static void send_page(struct mxc_nand_host *host, uint8_t buf_id, - unsigned int ops) +static void send_page(struct mxc_nand_host *host, unsigned int ops) { - /* NANDFC buffer 0 is used for page read/write */ - writew(buf_id, host->regs + NFC_BUF_ADDR); + int bufs, i; - writew(ops, host->regs + NFC_CONFIG2); + if (host->pagesize_2k) + bufs = 4; + else + bufs = 1; - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, true); + for (i = 0; i < bufs; i++) { + + /* NANDFC buffer 0 is used for page read/write */ + writew(i, host->regs + NFC_BUF_ADDR); + + writew(ops, host->regs + NFC_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, true); + } } /* Request the NANDFC to perform a read of the NAND device ID. */ @@ -565,16 +574,10 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); - if (host->pagesize_2k) { - /* send read confirm command */ + if (host->pagesize_2k) send_cmd(host, NAND_CMD_READSTART, true); - /* read for each AREA */ - send_page(host, 0, NFC_OUTPUT); - send_page(host, 1, NFC_OUTPUT); - send_page(host, 2, NFC_OUTPUT); - send_page(host, 3, NFC_OUTPUT); - } else - send_page(host, 0, NFC_OUTPUT); + + send_page(host, NFC_OUTPUT); memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); copy_spare(mtd, true); @@ -614,15 +617,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_PAGEPROG: memcpy(host->regs + MAIN_AREA0, host->data_buf, mtd->writesize); copy_spare(mtd, false); - send_page(host, 0, NFC_INPUT); - - if (host->pagesize_2k) { - /* data in 4 areas datas */ - send_page(host, 1, NFC_INPUT); - send_page(host, 2, NFC_INPUT); - send_page(host, 3, NFC_INPUT); - } - + send_page(host, NFC_INPUT); send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; -- cgit v1.2.3 From 0e60c7c4015f051ff921e6c30844f31e17f8ad95 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 5 Jun 2009 10:55:32 +0200 Subject: mxc_nand: remove unused defines Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 1131ad126fd2..ab6db698cbd8 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -122,13 +122,6 @@ struct mxc_nand_host { /* Define delays in microsec for NAND device operations */ #define TROP_US_DELAY 2000 -/* Macros to get byte and bit positions of ECC */ -#define COLPOS(x) ((x) >> 3) -#define BITPOS(x) ((x) & 0xf) - -/* Define single bit Error positions in Main & Spare area */ -#define MAIN_SINGLEBIT_ERROR 0x4 -#define SPARE_SINGLEBIT_ERROR 0x1 /* OOB placement block for use with hardware ecc generation */ static struct nand_ecclayout nand_hw_eccoob_smallpage = { -- cgit v1.2.3 From c6de7e1bb8d6e1b3ae318bf9fbe84557d7c8baec Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 5 Oct 2009 11:14:35 +0200 Subject: mxc_nand: Make main/spare areas runtime configurable The main/spare areas are on different addresses on later versions of the controller, so make them configurable. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index ab6db698cbd8..c84fd6c35496 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -52,18 +52,6 @@ #define NFC_CONFIG1 0xE1A #define NFC_CONFIG2 0xE1C -/* Addresses for NFC RAM BUFFER Main area 0 */ -#define MAIN_AREA0 0x000 -#define MAIN_AREA1 0x200 -#define MAIN_AREA2 0x400 -#define MAIN_AREA3 0x600 - -/* Addresses for NFC SPARE BUFFER Spare area 0 */ -#define SPARE_AREA0 0x800 -#define SPARE_AREA1 0x810 -#define SPARE_AREA2 0x820 -#define SPARE_AREA3 0x830 - /* Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register * for Command operation */ #define NFC_CMD 0x1 @@ -106,6 +94,11 @@ struct mxc_nand_host { struct mtd_partition *parts; struct device *dev; + void *spare0; + void *main_area0; + void *main_area1; + + void __iomem *base; void __iomem *regs; int status_request; int pagesize_2k; @@ -262,7 +255,7 @@ static void send_read_id(struct mxc_nand_host *host) wait_op_done(host, TROP_US_DELAY, true); if (this->options & NAND_BUSWIDTH_16) { - void __iomem *main_buf = host->regs + MAIN_AREA0; + void __iomem *main_buf = host->main_area0; /* compress the ID info */ writeb(readb(main_buf + 2), main_buf + 1); writeb(readb(main_buf + 4), main_buf + 2); @@ -270,14 +263,14 @@ static void send_read_id(struct mxc_nand_host *host) writeb(readb(main_buf + 8), main_buf + 4); writeb(readb(main_buf + 10), main_buf + 5); } - memcpy(host->data_buf, host->regs + MAIN_AREA0, 16); + memcpy(host->data_buf, host->main_area0, 16); } /* This function requests the NANDFC to perform a read of the * NAND device status and returns the current status. */ static uint16_t get_dev_status(struct mxc_nand_host *host) { - void __iomem *main_buf = host->regs + MAIN_AREA1; + void __iomem *main_buf = host->main_area1; uint32_t store; uint16_t ret, tmp; /* Issue status request to NAND device */ @@ -462,7 +455,7 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom) u16 i, j; u16 n = mtd->writesize >> 9; u8 *d = host->data_buf + mtd->writesize; - u8 *s = host->regs + SPARE_AREA0; + u8 *s = host->spare0; u16 t = host->spare_len; j = (mtd->oobsize / n >> 1) << 1; @@ -572,7 +565,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_page(host, NFC_OUTPUT); - memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); + memcpy(host->data_buf, host->main_area0, mtd->writesize); copy_spare(mtd, true); break; @@ -608,7 +601,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_PAGEPROG: - memcpy(host->regs + MAIN_AREA0, host->data_buf, mtd->writesize); + memcpy(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); send_page(host, NFC_INPUT); send_cmd(host, command, true); @@ -686,12 +679,17 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto eres; } - host->regs = ioremap(res->start, resource_size(res)); - if (!host->regs) { + host->base = ioremap(res->start, resource_size(res)); + if (!host->base) { err = -ENOMEM; goto eres; } + host->regs = host->base; + host->main_area0 = host->base; + host->main_area1 = host->base + 0x200; + host->spare0 = host->base + 0x800; + tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_INT_MSK; writew(tmp, host->regs + NFC_CONFIG1); @@ -778,7 +776,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) escan: free_irq(host->irq, host); eirq: - iounmap(host->regs); + iounmap(host->base); eres: clk_put(host->clk); eclk: @@ -797,7 +795,7 @@ static int __exit mxcnd_remove(struct platform_device *pdev) nand_release(&host->mtd); free_irq(host->irq, host); - iounmap(host->regs); + iounmap(host->base); kfree(host); return 0; -- cgit v1.2.3 From 2d69c7fadd8580a7cea3ae0caa90a1c2a92ab021 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 5 Oct 2009 11:24:02 +0200 Subject: mxc_nand: Get rid of pagesize_2k flag Later versions of this controller also allow 4k pagesize, so use mtd->writesize instead of a flag. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index c84fd6c35496..09b9aab6823d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -101,7 +101,6 @@ struct mxc_nand_host { void __iomem *base; void __iomem *regs; int status_request; - int pagesize_2k; struct clk *clk; int clk_act; int irq; @@ -214,11 +213,13 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) wait_op_done(host, TROP_US_DELAY, islast); } -static void send_page(struct mxc_nand_host *host, unsigned int ops) +static void send_page(struct mtd_info *mtd, unsigned int ops) { + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; int bufs, i; - if (host->pagesize_2k) + if (mtd->writesize > 512) bufs = 4; else bufs = 1; @@ -490,7 +491,7 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) * the full page. */ send_addr(host, 0, page_addr == -1); - if (host->pagesize_2k) + if (mtd->writesize > 512) /* another col addr cycle for 2k page */ send_addr(host, 0, false); } @@ -500,7 +501,7 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) /* paddr_0 - p_addr_7 */ send_addr(host, (page_addr & 0xff), false); - if (host->pagesize_2k) { + if (mtd->writesize > 512) { if (mtd->size >= 0x10000000) { /* paddr_8 - paddr_15 */ send_addr(host, (page_addr >> 8) & 0xff, false); @@ -554,16 +555,16 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, else host->buf_start = column + mtd->writesize; - if (host->pagesize_2k) + if (mtd->writesize > 512) command = NAND_CMD_READ0; /* only READ0 is valid */ send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); - if (host->pagesize_2k) + if (mtd->writesize > 512) send_cmd(host, NAND_CMD_READSTART, true); - send_page(host, NFC_OUTPUT); + send_page(mtd, NFC_OUTPUT); memcpy(host->data_buf, host->main_area0, mtd->writesize); copy_spare(mtd, true); @@ -578,7 +579,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, * pointer to spare area, we must write the whole page * including OOB together. */ - if (host->pagesize_2k) + if (mtd->writesize > 512) /* call ourself to read a page */ mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); @@ -586,13 +587,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, host->buf_start = column; /* Set program pointer to spare region */ - if (!host->pagesize_2k) + if (mtd->writesize == 512) send_cmd(host, NAND_CMD_READOOB, false); } else { host->buf_start = column; /* Set program pointer to page start */ - if (!host->pagesize_2k) + if (mtd->writesize == 512) send_cmd(host, NAND_CMD_READ0, false); } @@ -603,7 +604,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_PAGEPROG: memcpy(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); - send_page(host, NFC_INPUT); + send_page(mtd, NFC_INPUT); send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -745,10 +746,8 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } - if (mtd->writesize == 2048) { - host->pagesize_2k = 1; + if (mtd->writesize == 2048) this->ecc.layout = &nand_hw_eccoob_largepage; - } /* second phase scan */ if (nand_scan_tail(mtd)) { -- cgit v1.2.3 From 9467114ef43c971f0ae8aee3729d412125a2f432 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 5 Oct 2009 12:14:21 +0200 Subject: mxc_nand: Add NFC V2 support The v2 version of this controller is used on i.MX35/25 SoCs. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 81 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 13 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 09b9aab6823d..4a696820d6d4 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -33,9 +33,13 @@ #include #include +#include #define DRIVER_NAME "mxc_nand" +#define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35()) +#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27()) + /* Addresses for NFC registers */ #define NFC_BUF_SIZE 0xE00 #define NFC_BUF_ADDR 0xE04 @@ -46,8 +50,10 @@ #define NFC_RSLTMAIN_AREA 0xE0E #define NFC_RSLTSPARE_AREA 0xE10 #define NFC_WRPROT 0xE12 -#define NFC_UNLOCKSTART_BLKADDR 0xE14 -#define NFC_UNLOCKEND_BLKADDR 0xE16 +#define NFC_V1_UNLOCKSTART_BLKADDR 0xe14 +#define NFC_V1_UNLOCKEND_BLKADDR 0xe16 +#define NFC_V21_UNLOCKSTART_BLKADDR 0xe20 +#define NFC_V21_UNLOCKEND_BLKADDR 0xe22 #define NFC_NF_WRPRST 0xE18 #define NFC_CONFIG1 0xE1A #define NFC_CONFIG2 0xE1C @@ -116,19 +122,47 @@ struct mxc_nand_host { #define TROP_US_DELAY 2000 /* OOB placement block for use with hardware ecc generation */ -static struct nand_ecclayout nand_hw_eccoob_smallpage = { +static struct nand_ecclayout nandv1_hw_eccoob_smallpage = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, .oobfree = {{0, 5}, {12, 4}, } }; -static struct nand_ecclayout nand_hw_eccoob_largepage = { +static struct nand_ecclayout nandv1_hw_eccoob_largepage = { .eccbytes = 20, .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, } }; +/* OOB description for 512 byte pages with 16 byte OOB */ +static struct nand_ecclayout nandv2_hw_eccoob_smallpage = { + .eccbytes = 1 * 9, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15 + }, + .oobfree = { + {.offset = 0, .length = 5} + } +}; + +/* OOB description for 2048 byte pages with 64 byte OOB */ +static struct nand_ecclayout nandv2_hw_eccoob_largepage = { + .eccbytes = 4 * 9, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15, + 23, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 40, 41, 42, 43, 44, 45, 46, 47, + 55, 56, 57, 58, 59, 60, 61, 62, 63 + }, + .oobfree = { + {.offset = 2, .length = 4}, + {.offset = 16, .length = 7}, + {.offset = 32, .length = 7}, + {.offset = 48, .length = 7} + } +}; + #ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; #endif @@ -219,7 +253,7 @@ static void send_page(struct mtd_info *mtd, unsigned int ops) struct mxc_nand_host *host = nand_chip->priv; int bufs, i; - if (mtd->writesize > 512) + if (nfc_is_v1() && mtd->writesize > 512) bufs = 4; else bufs = 1; @@ -613,6 +647,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); send_read_id(host); + host->buf_start = column; break; case NAND_CMD_ERASE1: @@ -633,6 +668,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) struct resource *res; uint16_t tmp; int err = 0, nr_parts = 0; + struct nand_ecclayout *oob_smallpage, *oob_largepage; /* Allocate memory for MTD device structure and private data */ host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + @@ -641,7 +677,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) return -ENOMEM; host->data_buf = (uint8_t *)(host + 1); - host->spare_len = 16; host->dev = &pdev->dev; /* structures must be linked */ @@ -686,10 +721,23 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto eres; } - host->regs = host->base; host->main_area0 = host->base; host->main_area1 = host->base + 0x200; - host->spare0 = host->base + 0x800; + + if (nfc_is_v21()) { + host->regs = host->base + 0x1000; + host->spare0 = host->base + 0x1000; + host->spare_len = 64; + oob_smallpage = &nandv2_hw_eccoob_smallpage; + oob_largepage = &nandv2_hw_eccoob_largepage; + } else if (nfc_is_v1()) { + host->regs = host->base; + host->spare0 = host->base + 0x800; + host->spare_len = 16; + oob_smallpage = &nandv1_hw_eccoob_smallpage; + oob_largepage = &nandv1_hw_eccoob_largepage; + } else + BUG(); tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_INT_MSK; @@ -711,15 +759,22 @@ static int __init mxcnd_probe(struct platform_device *pdev) writew(0x2, host->regs + NFC_CONFIG); /* Blocks to be unlocked */ - writew(0x0, host->regs + NFC_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_UNLOCKEND_BLKADDR); + if (nfc_is_v21()) { + writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); + writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); + this->ecc.bytes = 9; + } else if (nfc_is_v1()) { + writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); + writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); + this->ecc.bytes = 3; + } else + BUG(); /* Unlock Block Command for given address range */ writew(0x4, host->regs + NFC_WRPROT); this->ecc.size = 512; - this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_smallpage; + this->ecc.layout = oob_smallpage; if (pdata->hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; @@ -747,7 +802,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) } if (mtd->writesize == 2048) - this->ecc.layout = &nand_hw_eccoob_largepage; + this->ecc.layout = oob_largepage; /* second phase scan */ if (nand_scan_tail(mtd)) { -- cgit v1.2.3 From f06368f7d15f6fc323ba0c71aec67b9b2dd5614a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 5 Oct 2009 17:18:42 +0200 Subject: mxc_nand: disable sp_en bit only once Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 4a696820d6d4..2e3f3fbcd67c 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -274,16 +274,10 @@ static void send_page(struct mtd_info *mtd, unsigned int ops) static void send_read_id(struct mxc_nand_host *host) { struct nand_chip *this = &host->nand; - uint16_t tmp; /* NANDFC buffer 0 is used for device ID output */ writew(0x0, host->regs + NFC_BUF_ADDR); - /* Read ID into main buffer */ - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_SP_EN; - writew(tmp, host->regs + NFC_CONFIG1); - writew(NFC_ID, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ @@ -307,7 +301,7 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) { void __iomem *main_buf = host->main_area1; uint32_t store; - uint16_t ret, tmp; + uint16_t ret; /* Issue status request to NAND device */ /* store the main area1 first word, later do recovery */ @@ -316,11 +310,6 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) * corruption of read/write buffer on status requests. */ writew(1, host->regs + NFC_BUF_ADDR); - /* Read status into main buffer */ - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_SP_EN; - writew(tmp, host->regs + NFC_CONFIG1); - writew(NFC_STATUS, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ @@ -739,8 +728,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) } else BUG(); + /* disable interrupt and spare enable */ tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_INT_MSK; + tmp &= ~NFC_SP_EN; writew(tmp, host->regs + NFC_CONFIG1); init_waitqueue_head(&host->irq_waitq); -- cgit v1.2.3 From f1372055df21734f042f12ab92852e9d350be8d0 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 14:25:27 +0200 Subject: mxc_nand: Allow flash based bbt Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 2e3f3fbcd67c..044a57483027 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -648,6 +648,33 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, } } +/* + * The generic flash bbt decriptors overlap with our ecc + * hardware, so define some i.MX specific ones. + */ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + static int __init mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; @@ -786,6 +813,13 @@ static int __init mxcnd_probe(struct platform_device *pdev) if (pdata->width == 2) this->options |= NAND_BUSWIDTH_16; + if (pdata->flash_bbt) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + /* update flash based bbt */ + this->options |= NAND_USE_FLASH_BBT; + } + /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1)) { err = -ENXIO; -- cgit v1.2.3 From c110eaf4659efcb110de2d7c90878240be5a13fa Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 16:01:02 +0200 Subject: mxc_nand: remove TROP_US_DELAY wait_op_done is only called with the same timeout, so code the timeout into the function itself. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 044a57483027..30eeb4c65528 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -118,9 +118,6 @@ struct mxc_nand_host { int spare_len; }; -/* Define delays in microsec for NAND device operations */ -#define TROP_US_DELAY 2000 - /* OOB placement block for use with hardware ecc generation */ static struct nand_ecclayout nandv1_hw_eccoob_smallpage = { .eccbytes = 5, @@ -185,10 +182,10 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) /* This function polls the NANDFC to wait for the basic operation to * complete by checking the INT bit of config2 register. */ -static void wait_op_done(struct mxc_nand_host *host, int max_retries, - int useirq) +static void wait_op_done(struct mxc_nand_host *host, int useirq) { uint32_t tmp; + int max_retries = 2000; if (useirq) { if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) { @@ -230,7 +227,7 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(NFC_CMD, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, useirq); + wait_op_done(host, useirq); } /* This function sends an address (or partial address) to the @@ -244,7 +241,7 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) writew(NFC_ADDR, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, islast); + wait_op_done(host, islast); } static void send_page(struct mtd_info *mtd, unsigned int ops) @@ -266,7 +263,7 @@ static void send_page(struct mtd_info *mtd, unsigned int ops) writew(ops, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, true); + wait_op_done(host, true); } } @@ -281,7 +278,7 @@ static void send_read_id(struct mxc_nand_host *host) writew(NFC_ID, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, true); + wait_op_done(host, true); if (this->options & NAND_BUSWIDTH_16) { void __iomem *main_buf = host->main_area0; @@ -313,7 +310,7 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) writew(NFC_STATUS, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, true); + wait_op_done(host, true); /* Status is placed in first word of main buffer */ /* get status, then recovery area 1 data */ -- cgit v1.2.3 From 1fbff0a6e975a986032881f139b806c23680f823 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 16:06:27 +0200 Subject: mxc_nand: use DRIVER_NAME where appropriate Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 30eeb4c65528..53e94121c26f 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -698,7 +698,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) mtd->priv = this; mtd->owner = THIS_MODULE; mtd->dev.parent = &pdev->dev; - mtd->name = "mxc_nand"; + mtd->name = DRIVER_NAME; /* 50 us command delay time */ this->chip_delay = 5; @@ -762,7 +762,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); - err = request_irq(host->irq, mxc_nfc_irq, 0, "mxc_nd", host); + err = request_irq(host->irq, mxc_nfc_irq, 0, DRIVER_NAME, host); if (err) goto eirq; -- cgit v1.2.3 From 1c63aca32903efc219fb9df72bae5344f3e54ed5 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 22 Oct 2009 16:53:32 +0900 Subject: mtd: Add __nand_calculate_ecc() to NAND ECC functions Add __nand_calculate_ecc() which does not take struct mtd_info. The built-in 256/512 software ECC calculation and correction tester will use it. Signed-off-by: Akinobu Mita Acked-by: Vimal Singh Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_ecc.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index db7ae9d6a296..809fb53304ae 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -150,20 +150,19 @@ static const char addressbits[256] = { }; /** - * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte + * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte * block - * @mtd: MTD block structure * @buf: input buffer with raw data + * @eccsize: data bytes per ecc step (256 or 512) * @code: output buffer with ECC */ -int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, +void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, unsigned char *code) { int i; const uint32_t *bp = (uint32_t *)buf; /* 256 or 512 bytes/ecc */ - const uint32_t eccsize_mult = - (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; + const uint32_t eccsize_mult = eccsize >> 8; uint32_t cur; /* current value in buffer */ /* rp0..rp15..rp17 are the various accumulated parities (per byte) */ uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; @@ -412,6 +411,22 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, (invparity[par & 0x55] << 2) | (invparity[rp17] << 1) | (invparity[rp16] << 0); +} +EXPORT_SYMBOL(__nand_calculate_ecc); + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte + * block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + __nand_calculate_ecc(buf, + ((struct nand_chip *)mtd->priv)->ecc.size, code); + return 0; } EXPORT_SYMBOL(nand_calculate_ecc); -- cgit v1.2.3 From 2af7c653993199fb32dc6d9504064a0c9c57e5d6 Mon Sep 17 00:00:00 2001 From: Simon Kagstrom Date: Mon, 5 Oct 2009 15:55:52 +0200 Subject: mtd: Add panic_write for NAND flashes This is a quick and dirty patch to add panic_write for NAND flashes. The patch seems to work OK on my CRIS board running a 2.6.26 kernel with a ID: 0x20, Chip ID: 0xf1 (ST Micro NAND 128MiB 3,3V 8-bit), and also on a OpenRD base (Marvell Kirkwood) board with a Toshiba NAND 512MiB 3,3V 8-bit flash with 2.6.32-pre1. Signed-off-by: Edgar E. Iglesias Signed-off-by: Simon Kagstrom Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 128 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 8 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2957cc70da3d..ba06473326d1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -428,6 +428,28 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, return nand_isbad_bbt(mtd, ofs, allowbbt); } +/** + * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands. + * @mtd: MTD device structure + * @timeo: Timeout + * + * Helper function for nand_wait_ready used when needing to wait in interrupt + * context. + */ +static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) +{ + struct nand_chip *chip = mtd->priv; + int i; + + /* Wait for the device to get ready */ + for (i = 0; i < timeo; i++) { + if (chip->dev_ready(mtd)) + break; + touch_softlockup_watchdog(); + mdelay(1); + } +} + /* * Wait for the ready pin, after a command * The timeout is catched later. @@ -437,6 +459,10 @@ void nand_wait_ready(struct mtd_info *mtd) struct nand_chip *chip = mtd->priv; unsigned long timeo = jiffies + 2; + /* 400ms timeout */ + if (in_interrupt() || oops_in_progress) + return panic_nand_wait_ready(mtd, 400); + led_trigger_event(nand_led_trigger, LED_FULL); /* wait until command is processed or timeout occures */ do { @@ -671,6 +697,22 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, nand_wait_ready(mtd); } +/** + * panic_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 + * + * Used when in panic, no locks are taken. + */ +static void panic_nand_get_device(struct nand_chip *chip, + struct mtd_info *mtd, int new_state) +{ + /* Hardware controller shared among independend devices */ + chip->controller->active = chip; + chip->state = new_state; +} + /** * nand_get_device - [GENERIC] Get chip for selected access * @chip: the nand chip descriptor @@ -709,6 +751,32 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) goto retry; } +/** + * panic_nand_wait - [GENERIC] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * @timeo: Timeout + * + * Wait for command done. This is a helper function for nand_wait used when + * we are in interrupt context. May happen when in panic and trying to write + * an oops trough mtdoops. + */ +static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, + unsigned long timeo) +{ + int i; + for (i = 0; i < timeo; i++) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + mdelay(1); + } +} + /** * nand_wait - [DEFAULT] wait until the command is done * @mtd: MTD device structure @@ -740,15 +808,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) else chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - while (time_before(jiffies, timeo)) { - if (chip->dev_ready) { - if (chip->dev_ready(mtd)) - break; - } else { - if (chip->read_byte(mtd) & NAND_STATUS_READY) - break; + if (in_interrupt() || oops_in_progress) + panic_nand_wait(mtd, chip, timeo); + else { + while (time_before(jiffies, timeo)) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + cond_resched(); } - cond_resched(); } led_trigger_event(nand_led_trigger, LED_OFF); @@ -1948,6 +2020,45 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, return ret; } +/** + * panic_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. Used when performing writes in interrupt context, this + * may for example be called by mtdoops when writing an oops while in panic. + */ +static int panic_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; + + /* Wait for the device to get ready. */ + panic_nand_wait(mtd, chip, 400); + + /* Grab the device. */ + panic_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; + return ret; +} + /** * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure @@ -2877,6 +2988,7 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->unpoint = NULL; mtd->read = nand_read; mtd->write = nand_write; + mtd->panic_write = panic_nand_write; mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; mtd->sync = nand_sync; -- cgit v1.2.3 From 6eb4feffb9d619a44fe434e777ef095a29cf4759 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Mon, 2 Nov 2009 23:40:48 +0900 Subject: mtd: txx9ndfmc: Use nand_release to free resources This patch fixes memory leak on chip->bbt and chip->buffers. Signed-off-by: Atsushi Nemoto Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/txx9ndfmc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 73af8324d0d0..863513c3b69a 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -429,11 +429,10 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) chip = mtd->priv; txx9_priv = chip->priv; + nand_release(mtd); #ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(mtd); kfree(drvdata->parts[i]); #endif - del_mtd_device(mtd); kfree(txx9_priv->mtdname); kfree(txx9_priv); } -- cgit v1.2.3 From b1c6e6db5bb7acad82e1c64914c6a9404dae3ee1 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 2 Nov 2009 18:12:33 +0000 Subject: mtd: nand: add option to quieten off the no device found messgae Add NAND_SCAN_SILENT_NODEV to chip->options to the user-worrying messages 'No NAND device found!!!'. This message often worries users (was three exclamation marks really necessary?) and especially in systems such as the Simtec Osiris where there may be optional NAND devices which are not known until probe time. Revised version of the original NAND_PROBE_SPECULATIVE patch after comments by Artem Bityutskiy about adding a whole new call. Signed-off-by: Ben Dooks Signed-off-by: Simtec Linux Team Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ba06473326d1..724cb2c9ad3f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2756,7 +2756,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); if (IS_ERR(type)) { - printk(KERN_WARNING "No NAND device found!!!\n"); + if (!(chip->options & NAND_SCAN_SILENT_NODEV)) + printk(KERN_WARNING "No NAND device found.\n"); chip->select_chip(mtd, -1); return PTR_ERR(type); } -- cgit v1.2.3 From 74218fedf478323cce831b51507eebd1faf0bf7f Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 2 Nov 2009 18:12:51 +0000 Subject: mtd: s3c2410: propagate nand options from the platform data Update the nand information passed to the core from the platform data to setup the initial option value, so that flags such as NAND_SCAN_SILENT_NODEV can pass through. Signed-off-by: Ben Dooks Signed-off-by: Simtec Linux Team Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/s3c2410.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 11dc7e69c4fb..6e88bd3c02f5 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -774,7 +774,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->select_chip = s3c2410_nand_select_chip; chip->chip_delay = 50; chip->priv = nmtd; - chip->options = 0; + chip->options = set->options; chip->controller = &info->controller; switch (info->cpu_type) { -- cgit v1.2.3 From 35016dd7e92cf0eb5ae2e811445dd52320c348d0 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 3 Nov 2009 20:49:18 +0100 Subject: mtd: ensure index is positive The index is signed, make sure it is not negative when we read the array element. Signed-off-by: Roel Kluin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_upm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index d120cd8d7267..071a60cb4204 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -112,7 +112,7 @@ static void fun_select_chip(struct mtd_info *mtd, int mchip_nr) if (mchip_nr == -1) { chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); - } else if (mchip_nr >= 0) { + } else if (mchip_nr >= 0 && mchip_nr < NAND_MAX_CHIPS) { fun->mchip_number = mchip_nr; chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr]; chip->IO_ADDR_W = chip->IO_ADDR_R; -- cgit v1.2.3 From 895fb49459227edbb4a4e5a2b5e9d12c34640f84 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 11 Nov 2009 21:47:06 +0100 Subject: mtd: error return -EIO instead of EIO Return a negative error value instead of a positive Signed-off-by: Roel Kluin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/atmel_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 4f62d207b87b..524e6c9e0672 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -463,7 +463,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if (host->board->det_pin) { if (gpio_get_value(host->board->det_pin)) { printk(KERN_INFO "No SmartMedia card inserted.\n"); - res = ENXIO; + res = -ENXIO; goto err_no_card; } } @@ -534,7 +534,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if ((!partitions) || (num_partitions == 0)) { printk(KERN_ERR "atmel_nand: No partitions defined, or unsupported device.\n"); - res = ENXIO; + res = -ENXIO; goto err_no_partitions; } -- cgit v1.2.3 From 54c69cc25064c1d333a12b5883aaa3bfa3041dee Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 13 Nov 2009 16:02:22 +0900 Subject: mtd: alauda: Use hweight8 Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/alauda.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 6d9649159a18..2d6773281fd9 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -372,15 +372,6 @@ static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob) return __alauda_read_page(mtd, from, ignore_buf, oob); } -static int popcount8(u8 c) -{ - int ret = 0; - - for ( ; c; c>>=1) - ret += c & 1; - return ret; -} - static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) { u8 oob[16]; @@ -391,7 +382,7 @@ static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) return err; /* A block is marked bad if two or more bits are zero */ - return popcount8(oob[5]) >= 7 ? 0 : 1; + return hweight8(oob[5]) >= 7 ? 0 : 1; } static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, -- cgit v1.2.3 From b3a70f0bc32d1b70584bcaa6019fa4260b0da92e Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 13 Nov 2009 14:12:16 -0600 Subject: mtd: eLBC NAND: increase bus timeout to maximum When a NAND operation is in progress, all other localbus operations (including NOR flash) will have to wait for access to the bus. However, the NAND operation may take longer to complete than the default timeout. Thus, if NOR is accessed while a NAND operation is in progress, the NAND operation will fail. Signed-off-by: Scott Wood Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_elbc_nand.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index ddd37d2554ed..58db27855126 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -946,6 +946,13 @@ static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl) { struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + /* + * NAND transactions can tie up the bus for a long time, so set the + * bus timeout to max by clearing LBCR[BMT] (highest base counter + * value) and setting LBCR[BMTPS] to the highest prescaler value. + */ + clrsetbits_be32(&lbc->lbcr, LBCR_BMT, 15); + /* clear event registers */ setbits32(&lbc->ltesr, LTESR_NAND_MASK); out_be32(&lbc->lteatr, 0); -- cgit v1.2.3 From 476459a6cf46d20ec73d9b211f3894ced5f9871e Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 13 Nov 2009 14:13:01 -0600 Subject: mtd: eLBC NAND: use recommended command sequences Currently, the program and erase sequences do not wait for completion, instead relying on a subsequent waitfunc() callback. However, this causes the chipselect to be deasserted while the NAND chip is still asserting the busy pin, which can corrupt activity on other chipselects. This patch switches to using the sequences recommended by the manual, in which a wait is performed within the initial command sequence. We can now re-use the status byte from the initial command sequence, rather than having to do another status read in the waitfunc. Since we're already touching the command sequences, it also cleans up some cruft in SEQIN that isn't needed since we cannot program partial pages outside of OOB. Signed-off-by: Scott Wood Reported-by: Suchit Lepcha Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_elbc_nand.c | 66 ++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 39 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 58db27855126..5b51ed3cf71d 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -253,17 +253,17 @@ static void fsl_elbc_do_read(struct nand_chip *chip, int oob) if (priv->page_size) { out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_CA << FIR_OP1_SHIFT) | (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_CM1 << FIR_OP3_SHIFT) | (FIR_OP_RBW << FIR_OP4_SHIFT)); out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); } else { out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_CA << FIR_OP1_SHIFT) | (FIR_OP_PA << FIR_OP2_SHIFT) | (FIR_OP_RBW << FIR_OP3_SHIFT)); @@ -332,7 +332,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, case NAND_CMD_READID: dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); - out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | + out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_UA << FIR_OP1_SHIFT) | (FIR_OP_RBW << FIR_OP2_SHIFT)); out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); @@ -359,16 +359,20 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_PA << FIR_OP1_SHIFT) | - (FIR_OP_CM1 << FIR_OP2_SHIFT)); + (FIR_OP_CM2 << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RS << FIR_OP4_SHIFT)); out_be32(&lbc->fcr, (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | - (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT)); + (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | + (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT)); out_be32(&lbc->fbcr, 0); ctrl->read_bytes = 0; + ctrl->use_mdr = 1; fsl_elbc_run_command(mtd); return; @@ -383,40 +387,41 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, ctrl->column = column; ctrl->oob = 0; + ctrl->use_mdr = 1; - if (priv->page_size) { - fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | - (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); + fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | + (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) | + (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT); + if (priv->page_size) { out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM2 << FIR_OP0_SHIFT) | (FIR_OP_CA << FIR_OP1_SHIFT) | (FIR_OP_PA << FIR_OP2_SHIFT) | (FIR_OP_WB << FIR_OP3_SHIFT) | - (FIR_OP_CW1 << FIR_OP4_SHIFT)); + (FIR_OP_CM3 << FIR_OP4_SHIFT) | + (FIR_OP_CW1 << FIR_OP5_SHIFT) | + (FIR_OP_RS << FIR_OP6_SHIFT)); } else { - fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | - (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_CM2 << FIR_OP1_SHIFT) | (FIR_OP_CA << FIR_OP2_SHIFT) | (FIR_OP_PA << FIR_OP3_SHIFT) | (FIR_OP_WB << FIR_OP4_SHIFT) | - (FIR_OP_CW1 << FIR_OP5_SHIFT)); + (FIR_OP_CM3 << FIR_OP5_SHIFT) | + (FIR_OP_CW1 << FIR_OP6_SHIFT) | + (FIR_OP_RS << FIR_OP7_SHIFT)); if (column >= mtd->writesize) { /* OOB area --> READOOB */ column -= mtd->writesize; fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; ctrl->oob = 1; - } else if (column < 256) { + } else { + WARN_ON(column != 0); /* First 256 bytes --> READ0 */ fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; - } else { - /* Second 256 bytes --> READ1 */ - fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT; } } @@ -628,22 +633,6 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - - if (ctrl->status != LTESR_CC) - return NAND_STATUS_FAIL; - - /* Use READ_STATUS command, but wait for the device to be ready */ - ctrl->use_mdr = 0; - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_RBW << FIR_OP1_SHIFT)); - out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); - out_be32(&lbc->fbcr, 1); - set_addr(mtd, 0, 0, 0); - ctrl->read_bytes = 1; - - fsl_elbc_run_command(mtd); if (ctrl->status != LTESR_CC) return NAND_STATUS_FAIL; @@ -651,8 +640,7 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) /* The chip always seems to report that it is * write-protected, even when it is not. */ - setbits8(ctrl->addr, NAND_STATUS_WP); - return fsl_elbc_read_byte(mtd); + return (ctrl->mdr & 0xff) | NAND_STATUS_WP; } static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) -- cgit v1.2.3 From c1317f7163d287805b8e12af12d04a2a6ead865c Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 13 Nov 2009 14:14:15 -0600 Subject: mtd: eLBC NAND: give more verbose output on error We want error information even if the kernel hasn't been built for verbose debugging. Signed-off-by: Scott Wood Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_elbc_nand.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 5b51ed3cf71d..ae30fb6eed97 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -237,12 +237,15 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) ctrl->use_mdr = 0; - dev_vdbg(ctrl->dev, - "fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n", - ctrl->status, ctrl->mdr, in_be32(&lbc->fmr)); + if (ctrl->status != LTESR_CC) { + dev_info(ctrl->dev, + "command failed: fir %x fcr %x status %x mdr %x\n", + in_be32(&lbc->fir), in_be32(&lbc->fcr), + ctrl->status, ctrl->mdr); + return -EIO; + } - /* returns 0 on success otherwise non-zero) */ - return ctrl->status == LTESR_CC ? 0 : -EIO; + return 0; } static void fsl_elbc_do_read(struct nand_chip *chip, int oob) -- cgit v1.2.3 From 8fead79c96c68164372c47c7b3c79d2da0871c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Nov 2009 22:06:58 +0100 Subject: mtd: move excite_nand_remove to .devexit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function excite_nand_remove is used only wrapped by __devexit_p so define it using __devexit. Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/excite_nandflash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c index 72446fb48d4b..af6a6a5399e1 100644 --- a/drivers/mtd/nand/excite_nandflash.c +++ b/drivers/mtd/nand/excite_nandflash.c @@ -128,7 +128,7 @@ static int excite_nand_devready(struct mtd_info *mtd) * The binding to the mtd and all allocated * resources are released. */ -static int __exit excite_nand_remove(struct platform_device *dev) +static int __devexit excite_nand_remove(struct platform_device *dev) { struct excite_nand_drvdata * const this = platform_get_drvdata(dev); -- cgit v1.2.3 From daa0f15a8dc6ef81655c7853f84a3670be7d144a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Nov 2009 22:07:08 +0100 Subject: mtd: don't use __exit_p to wrap mxcnd_remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function mxcnd_remove is defined using __devexit, so don't use __exit_p but __devexit_p to wrap it. Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 06c531485df9..4bc43eb6c604 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1099,7 +1099,7 @@ static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, }, - .remove = __exit_p(mxcnd_remove), + .remove = __devexit_p(mxcnd_remove), .suspend = mxcnd_suspend, .resume = mxcnd_resume, }; -- cgit v1.2.3 From f611a79fe9859a30f2a7ae94b4d24f8e2dd75c91 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Mon, 12 Oct 2009 16:16:37 -0700 Subject: mtd: Add bad block table overrides to Davinci NAND driver The existing NAND infrastructure allows the default main and mirror bad block tables to be overridden in nand_default_bbt(). However, the davinci_nand driver does not support this. Add that support by adding fields to the davinci driver's platform data so platform code can pass in their own bbt's and make the davinci_nand driver honor them. Signed-off-by: Mark A. Greer Signed-off-by: Kevin Hilman CC: Sudhakar Rajashekhara Signed-off-by: David Woodhouse --- drivers/mtd/nand/davinci_nand.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 68cc9247fdb0..fe3eba87de40 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -591,6 +591,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) /* options such as NAND_USE_FLASH_BBT or 16-bit widths */ info->chip.options = pdata->options; + info->chip.bbt_td = pdata->bbt_td; + info->chip.bbt_md = pdata->bbt_md; info->ioaddr = (uint32_t __force) vaddr; -- cgit v1.2.3 From 6b0d9a84124937f048bcb8b21313152b23063978 Mon Sep 17 00:00:00 2001 From: Li Yang Date: Tue, 17 Nov 2009 14:45:49 -0800 Subject: mtd: nand: fix multi-chip suspend problem Symptom: device_suspend(): mtd_cls_suspend+0x0/0x58 returns -11 PM: Device mtd14 failed to suspend: error -11 PM: Some devices failed to suspend This patch enables other chips to be suspended if the active chip of the controller has been suspended. Signed-off-by: Jin Qing Signed-off-by: Li Yang Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 724cb2c9ad3f..8f2958fe2148 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -740,8 +740,14 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) return 0; } if (new_state == FL_PM_SUSPENDED) { - spin_unlock(lock); - return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; + if (chip->controller->active->state == FL_PM_SUSPENDED) { + chip->state = FL_PM_SUSPENDED; + spin_unlock(lock); + return 0; + } else { + spin_unlock(lock); + return -EAGAIN; + } } set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(wq, &wait); -- cgit v1.2.3 From 98ecc914d0a48dbbcbb5fc866ab5b59a52afbd41 Mon Sep 17 00:00:00 2001 From: David Hunter Date: Mon, 23 Nov 2009 21:02:35 -0800 Subject: mtd: pxa3xx_nand: Fix PIO data transfer The shift operator used here to convert from bytes to 32-bit words is backwards. Signed-off-by: David Hunter Signed-off-by: David Woodhouse --- drivers/mtd/nand/pxa3xx_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 6ea520ae2410..75aa8713abd1 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -489,7 +489,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info) switch (info->state) { case STATE_PIO_WRITING: __raw_writesl(info->mmio_base + NDDB, info->data_buff, - info->data_size << 2); + info->data_size >> 2); enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); @@ -501,7 +501,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info) break; case STATE_PIO_READING: __raw_readsl(info->mmio_base + NDDB, info->data_buff, - info->data_size << 2); + info->data_size >> 2); break; default: printk(KERN_ERR "%s: invalid state %d\n", __func__, -- cgit v1.2.3 From 75352662c54421b48ed58200565395b123952748 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 29 Nov 2009 19:07:57 +0100 Subject: mtd: nandsim: add support for 4KiB pages I was going to play with a faulty nand image from real flash and noticed that nandsim does not work with: first_id_byte=0xec second_id_byte=0xd5 third_id_byte=0x51 fourth_id_byte=0xa6 This patch seems to fix it. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nandsim.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index cd0711b83ac4..7281000fef2d 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -161,7 +161,7 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); /* The largest possible page size */ -#define NS_LARGEST_PAGE_SIZE 2048 +#define NS_LARGEST_PAGE_SIZE 4096 /* The prefix for simulator output */ #define NS_OUTPUT_PREFIX "[nandsim]" @@ -259,7 +259,8 @@ MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of mem #define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ #define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */ #define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ -#define OPT_LARGEPAGE (OPT_PAGE2048) /* 2048-byte page chips */ +#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ +#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ #define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */ /* Remove action bits ftom state */ @@ -588,6 +589,8 @@ static int init_nandsim(struct mtd_info *mtd) ns->options |= OPT_PAGE512_8BIT; } else if (ns->geom.pgsz == 2048) { ns->options |= OPT_PAGE2048; + } else if (ns->geom.pgsz == 4096) { + ns->options |= OPT_PAGE4096; } else { NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); return -EIO; -- cgit v1.2.3 From 51eeb878155cdea73e7a1f0372b266018bd7a424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 7 Dec 2009 09:44:05 +0000 Subject: Revert "mtd: move mxcnd_remove to .exit.text" This reverts commit 82613b0da622efbd24cb7b23eb349966802310f1, because commit daa0f15 (mtd: don't use __exit_p to wrap mxcnd_remove) is a better solution. Not having a remove callback breaks rebinding because resources are not freed on remove. Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index f7366e99fe1e..45dec5770da0 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -861,7 +861,7 @@ eclk: return err; } -static int __exit mxcnd_remove(struct platform_device *pdev) +static int __devexit mxcnd_remove(struct platform_device *pdev) { struct mxc_nand_host *host = platform_get_drvdata(pdev); -- cgit v1.2.3