diff options
author | Len Brown <len.brown@intel.com> | 2012-04-06 21:48:59 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2012-04-06 21:48:59 -0400 |
commit | eeaab2d8af2cf1d36d7086f22e9de42d6dd2995c (patch) | |
tree | 369b9c91a6d808944f07d2290fec6f9fe2731904 /drivers/mtd/nand | |
parent | ee01e663373343c63e0e3d364d09f6155378dbcc (diff) | |
parent | aaef292acf3a78d9c0bb6fb72226077d286b45d7 (diff) |
Merge branches 'idle-fix' and 'misc' into release
Diffstat (limited to 'drivers/mtd/nand')
37 files changed, 3609 insertions, 504 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 31b034b7eba..7d17cecad69 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -187,7 +187,7 @@ config MTD_NAND_PPCHAMELEONEVB config MTD_NAND_S3C2410 tristate "NAND Flash support for Samsung S3C SoCs" - depends on ARCH_S3C2410 || ARCH_S3C64XX + depends on ARCH_S3C24XX || ARCH_S3C64XX help This enables the NAND flash controller on the S3C24xx and S3C64xx SoCs @@ -246,6 +246,7 @@ config MTD_NAND_BCM_UMI_HWCS config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" depends on EXPERIMENTAL + depends on HAS_IOMEM select REED_SOLOMON select REED_SOLOMON_DEC16 help @@ -313,6 +314,26 @@ config MTD_NAND_DISKONCHIP_BBTWRITE load time (assuming you build diskonchip as a module) with the module parameter "inftl_bbt_write=1". +config MTD_NAND_DOCG4 + tristate "Support for DiskOnChip G4 (EXPERIMENTAL)" + depends on EXPERIMENTAL + select BCH + select BITREVERSE + help + Support for diskonchip G4 nand flash, found in various smartphones and + PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba + Portege G900, Asus P526, and O2 XDA Zinc. + + With this driver you will be able to use UBI and create a ubifs on the + device, so you may wish to consider enabling UBI and UBIFS as well. + + These devices ship with the Mys/Sandisk SAFTL formatting, for which + there is currently no mtd parser, so you may want to use command line + partitioning to segregate write-protected blocks. On the Treo680, the + first five erase blocks (256KiB each) are write-protected, followed + by the block containing the saftl partition table. This is probably + typical. + config MTD_NAND_SHARPSL tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" depends on ARCH_PXA @@ -420,7 +441,6 @@ config MTD_NAND_NANDSIM config MTD_NAND_GPMI_NAND bool "GPMI NAND Flash Controller driver" depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28) - select MTD_CMDLINE_PARTS help Enables NAND Flash support for IMX23 or IMX28. The GPMI controller is very powerful, with the help of BCH @@ -431,6 +451,7 @@ config MTD_NAND_GPMI_NAND config MTD_NAND_PLATFORM tristate "Support for generic platform NAND driver" + depends on HAS_IOMEM help This implements a generic NAND driver for on-SOC platform devices. You will need to provide platform-specific functions @@ -462,6 +483,16 @@ config MTD_NAND_FSL_ELBC Enabling this option will enable you to use this to control external NAND devices. +config MTD_NAND_FSL_IFC + tristate "NAND support for Freescale IFC controller" + depends on MTD_NAND && FSL_SOC + select FSL_IFC + help + Various Freescale chips e.g P1010, include a NAND Flash machine + with built-in hardware ECC capabilities. + Enabling this option will enable you to use this to control + external NAND devices. + config MTD_NAND_FSL_UPM tristate "Support for NAND on Freescale UPM" depends on PPC_83xx || PPC_85xx diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 618f4ba2369..d4b4d8739bd 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o +obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o @@ -37,6 +38,7 @@ obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o +obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 6a5ff64a139..4f20e1d8bef 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -585,12 +585,13 @@ static int alauda_init_media(struct alauda *al) mtd->writesize = 1<<card->pageshift; mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->read = alauda_read; - mtd->write = alauda_write; - mtd->erase = alauda_erase; - mtd->block_isbad = alauda_isbad; + mtd->_read = alauda_read; + mtd->_write = alauda_write; + mtd->_erase = alauda_erase; + mtd->_block_isbad = alauda_isbad; mtd->priv = al; mtd->owner = THIS_MODULE; + mtd->ecc_strength = 1; err = mtd_device_register(mtd, NULL, 0); if (err) { diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 3197e9764fc..73416951f4c 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -26,7 +26,7 @@ #include <asm/io.h> #include <mach/hardware.h> #include <asm/sizes.h> -#include <asm/gpio.h> +#include <linux/gpio.h> #include <plat/board-ams-delta.h> /* @@ -34,8 +34,6 @@ */ static struct mtd_info *ams_delta_mtd = NULL; -#define NAND_MASK (AMS_DELTA_LATCH2_NAND_NRE | AMS_DELTA_LATCH2_NAND_NWE | AMS_DELTA_LATCH2_NAND_CLE | AMS_DELTA_LATCH2_NAND_ALE | AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP) - /* * Define partitions for flash devices */ @@ -68,10 +66,9 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte) writew(0, io_base + OMAP_MPUIO_IO_CNTL); writew(byte, this->IO_ADDR_W); - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0); ndelay(40); - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, - AMS_DELTA_LATCH2_NAND_NWE); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1); } static u_char ams_delta_read_byte(struct mtd_info *mtd) @@ -80,12 +77,11 @@ static u_char ams_delta_read_byte(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; void __iomem *io_base = this->priv; - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0); ndelay(40); writew(~0, io_base + OMAP_MPUIO_IO_CNTL); res = readw(this->IO_ADDR_R); - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, - AMS_DELTA_LATCH2_NAND_NRE); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1); return res; } @@ -132,15 +128,12 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd, { if (ctrl & NAND_CTRL_CHANGE) { - unsigned long bits; - - bits = (~ctrl & NAND_NCE) ? AMS_DELTA_LATCH2_NAND_NCE : 0; - bits |= (ctrl & NAND_CLE) ? AMS_DELTA_LATCH2_NAND_CLE : 0; - bits |= (ctrl & NAND_ALE) ? AMS_DELTA_LATCH2_NAND_ALE : 0; - - ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_CLE | - AMS_DELTA_LATCH2_NAND_ALE | - AMS_DELTA_LATCH2_NAND_NCE, bits); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE, + (ctrl & NAND_NCE) == 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE, + (ctrl & NAND_CLE) != 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE, + (ctrl & NAND_ALE) != 0); } if (cmd != NAND_CMD_NONE) @@ -152,6 +145,39 @@ static int ams_delta_nand_ready(struct mtd_info *mtd) return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB); } +static const struct gpio _mandatory_gpio[] = { + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nce", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nre", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nwp", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE, + .flags = GPIOF_OUT_INIT_HIGH, + .label = "nand_nwe", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE, + .flags = GPIOF_OUT_INIT_LOW, + .label = "nand_ale", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE, + .flags = GPIOF_OUT_INIT_LOW, + .label = "nand_cle", + }, +}; + /* * Main initialization routine */ @@ -223,10 +249,9 @@ static int __devinit ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, io_base); /* Set chip enabled, but */ - ams_delta_latch2_write(NAND_MASK, AMS_DELTA_LATCH2_NAND_NRE | - AMS_DELTA_LATCH2_NAND_NWE | - AMS_DELTA_LATCH2_NAND_NCE | - AMS_DELTA_LATCH2_NAND_NWP); + err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); + if (err) + goto out_gpio; /* Scan to find existence of the device */ if (nand_scan(ams_delta_mtd, 1)) { @@ -241,7 +266,10 @@ static int __devinit ams_delta_init(struct platform_device *pdev) goto out; out_mtd: + gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); +out_gpio: platform_set_drvdata(pdev, NULL); + gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); iounmap(io_base); out_release_io: release_mem_region(res->start, resource_size(res)); @@ -262,6 +290,8 @@ static int __devexit ams_delta_cleanup(struct platform_device *pdev) /* Release resources, unregister device */ nand_release(ams_delta_mtd); + gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); + gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); iounmap(io_base); release_mem_region(res->start, resource_size(res)); diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 35b4fb55dbd..2165576a1c6 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -27,6 +27,10 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_mtd.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> @@ -34,22 +38,10 @@ #include <linux/dmaengine.h> #include <linux/gpio.h> #include <linux/io.h> +#include <linux/platform_data/atmel.h> -#include <mach/board.h> #include <mach/cpu.h> -#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW -#define hard_ecc 1 -#else -#define hard_ecc 0 -#endif - -#ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE -#define no_ecc 1 -#else -#define no_ecc 0 -#endif - static int use_dma = 1; module_param(use_dma, int, 0); @@ -95,7 +87,7 @@ struct atmel_nand_host { struct mtd_info mtd; void __iomem *io_base; dma_addr_t io_phys; - struct atmel_nand_data *board; + struct atmel_nand_data board; struct device *dev; void __iomem *ecc; @@ -113,8 +105,8 @@ static int cpu_has_dma(void) */ static void atmel_nand_enable(struct atmel_nand_host *host) { - if (gpio_is_valid(host->board->enable_pin)) - gpio_set_value(host->board->enable_pin, 0); + if (gpio_is_valid(host->board.enable_pin)) + gpio_set_value(host->board.enable_pin, 0); } /* @@ -122,8 +114,8 @@ static void atmel_nand_enable(struct atmel_nand_host *host) */ static void atmel_nand_disable(struct atmel_nand_host *host) { - if (gpio_is_valid(host->board->enable_pin)) - gpio_set_value(host->board->enable_pin, 1); + if (gpio_is_valid(host->board.enable_pin)) + gpio_set_value(host->board.enable_pin, 1); } /* @@ -144,9 +136,9 @@ static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl return; if (ctrl & NAND_CLE) - writeb(cmd, host->io_base + (1 << host->board->cle)); + writeb(cmd, host->io_base + (1 << host->board.cle)); else - writeb(cmd, host->io_base + (1 << host->board->ale)); + writeb(cmd, host->io_base + (1 << host->board.ale)); } /* @@ -157,8 +149,8 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) struct nand_chip *nand_chip = mtd->priv; struct atmel_nand_host *host = nand_chip->priv; - return gpio_get_value(host->board->rdy_pin) ^ - !!host->board->rdy_pin_active_low; + return gpio_get_value(host->board.rdy_pin) ^ + !!host->board.rdy_pin_active_low; } /* @@ -273,7 +265,7 @@ static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) if (atmel_nand_dma_op(mtd, buf, len, 1) == 0) return; - if (host->board->bus_width_16) + if (host->board.bus_width_16) atmel_read_buf16(mtd, buf, len); else atmel_read_buf8(mtd, buf, len); @@ -289,7 +281,7 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0) return; - if (host->board->bus_width_16) + if (host->board.bus_width_16) atmel_write_buf16(mtd, buf, len); else atmel_write_buf8(mtd, buf, len); @@ -481,6 +473,56 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) } } +#if defined(CONFIG_OF) +static int __devinit atmel_of_init_port(struct atmel_nand_host *host, + struct device_node *np) +{ + u32 val; + int ecc_mode; + struct atmel_nand_data *board = &host->board; + enum of_gpio_flags flags; + + if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { + if (val >= 32) { + dev_err(host->dev, "invalid addr-offset %u\n", val); + return -EINVAL; + } + board->ale = val; + } + + if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) { + if (val >= 32) { + dev_err(host->dev, "invalid cmd-offset %u\n", val); + return -EINVAL; + } + board->cle = val; + } + + ecc_mode = of_get_nand_ecc_mode(np); + + board->ecc_mode = ecc_mode < 0 ? NAND_ECC_SOFT : ecc_mode; + + board->on_flash_bbt = of_get_nand_on_flash_bbt(np); + + if (of_get_nand_bus_width(np) == 16) + board->bus_width_16 = 1; + + board->rdy_pin = of_get_gpio_flags(np, 0, &flags); + board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW); + + board->enable_pin = of_get_gpio(np, 1); + board->det_pin = of_get_gpio(np, 2); + + return 0; +} +#else +static int __devinit atmel_of_init_port(struct atmel_nand_host *host, + struct device_node *np) +{ + return -EINVAL; +} +#endif + /* * Probe for the NAND device. */ @@ -491,6 +533,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) struct nand_chip *nand_chip; struct resource *regs; struct resource *mem; + struct mtd_part_parser_data ppdata = {}; int res; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -517,8 +560,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev) mtd = &host->mtd; nand_chip = &host->nand_chip; - host->board = pdev->dev.platform_data; host->dev = &pdev->dev; + if (pdev->dev.of_node) { + res = atmel_of_init_port(host, pdev->dev.of_node); + if (res) + goto err_nand_ioremap; + } else { + memcpy(&host->board, pdev->dev.platform_data, + sizeof(struct atmel_nand_data)); + } nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; @@ -529,36 +579,36 @@ static int __init atmel_nand_probe(struct platform_device *pdev) nand_chip->IO_ADDR_W = host->io_base; nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; - if (gpio_is_valid(host->board->rdy_pin)) + if (gpio_is_valid(host->board.rdy_pin)) nand_chip->dev_ready = atmel_nand_device_ready; + nand_chip->ecc.mode = host->board.ecc_mode; + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!regs && hard_ecc) { + if (!regs && nand_chip->ecc.mode == NAND_ECC_HW) { printk(KERN_ERR "atmel_nand: can't get I/O resource " "regs\nFalling back on software ECC\n"); + nand_chip->ecc.mode = NAND_ECC_SOFT; } - nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ - if (no_ecc) - nand_chip->ecc.mode = NAND_ECC_NONE; - if (hard_ecc && regs) { + if (nand_chip->ecc.mode == NAND_ECC_HW) { host->ecc = ioremap(regs->start, resource_size(regs)); if (host->ecc == NULL) { printk(KERN_ERR "atmel_nand: ioremap failed\n"); res = -EIO; goto err_ecc_ioremap; } - nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.calculate = atmel_nand_calculate; nand_chip->ecc.correct = atmel_nand_correct; nand_chip->ecc.hwctl = atmel_nand_hwctl; nand_chip->ecc.read_page = atmel_nand_read_page; nand_chip->ecc.bytes = 4; + nand_chip->ecc.strength = 1; } nand_chip->chip_delay = 20; /* 20us command delay time */ - if (host->board->bus_width_16) /* 16-bit bus width */ + if (host->board.bus_width_16) /* 16-bit bus width */ nand_chip->options |= NAND_BUSWIDTH_16; nand_chip->read_buf = atmel_read_buf; @@ -567,15 +617,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); atmel_nand_enable(host); - if (gpio_is_valid(host->board->det_pin)) { - if (gpio_get_value(host->board->det_pin)) { + if (gpio_is_valid(host->board.det_pin)) { + if (gpio_get_value(host->board.det_pin)) { printk(KERN_INFO "No SmartMedia card inserted.\n"); res = -ENXIO; goto err_no_card; } } - if (on_flash_bbt) { + if (host->board.on_flash_bbt || on_flash_bbt) { printk(KERN_INFO "atmel_nand: Use On Flash BBT\n"); nand_chip->bbt_options |= NAND_BBT_USE_FLASH; } @@ -650,8 +700,9 @@ static int __init atmel_nand_probe(struct platform_device *pdev) } mtd->name = "atmel_nand"; - res = mtd_device_parse_register(mtd, NULL, 0, - host->board->parts, host->board->num_parts); + ppdata.of_node = pdev->dev.of_node; + res = mtd_device_parse_register(mtd, NULL, &ppdata, + host->board.parts, host->board.num_parts); if (!res) return res; @@ -695,11 +746,21 @@ static int __exit atmel_nand_remove(struct platform_device *pdev) return 0; } +#if defined(CONFIG_OF) +static const struct of_device_id atmel_nand_dt_ids[] = { + { .compatible = "atmel,at91rm9200-nand" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_nand_dt_ids); +#endif + static struct platform_driver atmel_nand_driver = { .remove = __exit_p(atmel_nand_remove), .driver = { .name = "atmel_nand", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_nand_dt_ids), }, }; diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index 50387fd4009..6908cdde306 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -31,7 +31,6 @@ #include <linux/mtd/partitions.h> #include <asm/mach-types.h> -#include <asm/system.h> #include <mach/reg_nand.h> #include <mach/reg_umi.h> @@ -476,6 +475,14 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) largepage_bbt.options = NAND_BBT_SCAN2NDPAGE; this->badblock_pattern = &largepage_bbt; } + + /* + * FIXME: ecc strength value of 6 bits per 512 bytes of data is a + * conservative guess, given 13 ecc bytes and using bch alg. + * (Assume Galois field order m=15 to allow a margin of error.) + */ + this->ecc.strength = 6; + #endif /* Now finish off the scan, now that ecc.layout has been initialized. */ @@ -488,7 +495,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) /* Register the partitions */ board_mtd->name = "bcm_umi-nand"; - mtd_device_parse_register(board_mtd, NULL, 0, NULL, 0); + mtd_device_parse_register(board_mtd, NULL, NULL, NULL, 0); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index dd899cb5d36..d7b86b925de 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -702,9 +702,11 @@ static int bf5xx_nand_scan(struct mtd_info *mtd) if (likely(mtd->writesize >= 512)) { chip->ecc.size = 512; chip->ecc.bytes = 6; + chip->ecc.strength = 2; } else { chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET)); SSYNC(); } diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 72d3f23490c..2a96e1a1206 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -783,6 +783,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; cafe->nand.ecc.size = mtd->writesize; cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.strength = 4; cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; cafe->nand.ecc.calculate = (void *)cafe_nand_bug; cafe->nand.ecc.correct = (void *)cafe_nand_bug; @@ -799,7 +800,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mtd); mtd->name = "cafe_nand"; - mtd_device_parse_register(mtd, part_probes, 0, NULL, 0); + mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); goto out; diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 737ef9a04fd..1024bfc05c8 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -219,7 +219,7 @@ static int __init cmx270_init(void) } /* Register the partitions */ - ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, 0, + ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL, partition_info, NUM_PARTITIONS); if (ret) goto err_scan; diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 414afa79356..821c34c6250 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -248,6 +248,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) goto out_ior; } + this->ecc.strength = 1; + new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs); cs553x_mtd[cs] = new_mtd; @@ -313,7 +315,7 @@ static int __init cs553x_init(void) for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { if (cs553x_mtd[i]) { /* If any devices registered, return success. Else the last error. */ - mtd_device_parse_register(cs553x_mtd[i], NULL, 0, + mtd_device_parse_register(cs553x_mtd[i], NULL, NULL, NULL, 0); err = 0; } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 6e566156956..d94b03c207a 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -641,6 +641,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->chip.ecc.bytes = 3; } info->chip.ecc.size = 512; + info->chip.ecc.strength = pdata->ecc_bits; break; default: ret = -EINVAL; @@ -752,8 +753,8 @@ syndrome_done: if (ret < 0) goto err_scan; - ret = mtd_device_parse_register(&info->mtd, NULL, 0, - pdata->parts, pdata->nr_parts); + ret = mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts, + pdata->nr_parts); if (ret < 0) goto err_scan; diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 3984d488f9a..a9e57d68629 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1590,6 +1590,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ECC_15BITS * (denali->mtd.writesize / ECC_SECTOR_SIZE)))) { /* if MLC OOB size is large enough, use 15bit ECC*/ + denali->nand.ecc.strength = 15; denali->nand.ecc.layout = &nand_15bit_oob; denali->nand.ecc.bytes = ECC_15BITS; iowrite32(15, denali->flash_reg + ECC_CORRECTION); @@ -1600,12 +1601,14 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) " contain 8bit ECC correction codes"); goto failed_req_irq; } else { + denali->nand.ecc.strength = 8; denali->nand.ecc.layout = &nand_8bit_oob; denali->nand.ecc.bytes = ECC_8BITS; iowrite32(8, denali->flash_reg + ECC_CORRECTION); } denali->nand.ecc.bytes *= denali->devnum; + denali->nand.ecc.strength *= denali->devnum; denali->nand.ecc.layout->eccbytes *= denali->mtd.writesize / ECC_SECTOR_SIZE; denali->nand.ecc.layout->oobfree[0].offset = diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index df921e7a496..e2ca067631c 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1653,6 +1653,7 @@ static int __init doc_probe(unsigned long physadr) nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.size = 512; nand->ecc.bytes = 6; + nand->ecc.strength = 2; nand->bbt_options = NAND_BBT_USE_FLASH; doc->physadr = physadr; diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c new file mode 100644 index 00000000000..b0820266454 --- /dev/null +++ b/drivers/mtd/nand/docg4.c @@ -0,0 +1,1377 @@ +/* + * Copyright © 2012 Mike Dunn <mikedunn@newsguy.com> + * + * mtd nand driver for M-Systems DiskOnChip G4 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus + * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others. + * Should work on these as well. Let me know! + * + * TODO: + * + * Mechanism for management of password-protected areas + * + * Hamming ecc when reading oob only + * + * According to the M-Sys documentation, this device is also available in a + * "dual-die" configuration having a 256MB capacity, but no mechanism for + * detecting this variant is documented. Currently this driver assumes 128MB + * capacity. + * + * Support for multiple cascaded devices ("floors"). Not sure which gadgets + * contain multiple G4s in a cascaded configuration, if any. + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/bitops.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/bch.h> +#include <linux/bitrev.h> + +/* + * You'll want to ignore badblocks if you're reading a partition that contains + * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since + * it does not use mtd nand's method for marking bad blocks (using oob area). + * This will also skip the check of the "page written" flag. + */ +static bool ignore_badblocks; +module_param(ignore_badblocks, bool, 0); +MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed"); + +struct docg4_priv { + struct mtd_info *mtd; + struct device *dev; + void __iomem *virtadr; + int status; + struct { + unsigned int command; + int column; + int page; + } last_command; + uint8_t oob_buf[16]; + uint8_t ecc_buf[7]; + int oob_page; + struct bch_control *bch; +}; + +/* + * Defines prefixed with DOCG4 are unique to the diskonchip G4. All others are + * shared with other diskonchip devices (P3, G3 at least). + * + * Functions with names prefixed with docg4_ are mtd / nand interface functions + * (though they may also be called internally). All others are internal. + */ + +#define DOC_IOSPACE_DATA 0x0800 + +/* register offsets */ +#define DOC_CHIPID 0x1000 +#define DOC_DEVICESELECT 0x100a +#define DOC_ASICMODE 0x100c +#define DOC_DATAEND 0x101e +#define DOC_NOP 0x103e + +#define DOC_FLASHSEQUENCE 0x1032 +#define DOC_FLASHCOMMAND 0x1034 +#define DOC_FLASHADDRESS 0x1036 +#define DOC_FLASHCONTROL 0x1038 +#define DOC_ECCCONF0 0x1040 +#define DOC_ECCCONF1 0x1042 +#define DOC_HAMMINGPARITY 0x1046 +#define DOC_BCH_SYNDROM(idx) (0x1048 + idx) + +#define DOC_ASICMODECONFIRM 0x1072 +#define DOC_CHIPID_INV 0x1074 +#define DOC_POWERMODE 0x107c + +#define DOCG4_MYSTERY_REG 0x1050 + +/* apparently used only to write oob bytes 6 and 7 */ +#define DOCG4_OOB_6_7 0x1052 + +/* DOC_FLASHSEQUENCE register commands */ +#define DOC_SEQ_RESET 0x00 +#define DOCG4_SEQ_PAGE_READ 0x03 +#define DOCG4_SEQ_FLUSH 0x29 +#define DOCG4_SEQ_PAGEWRITE 0x16 +#define DOCG4_SEQ_PAGEPROG 0x1e +#define DOCG4_SEQ_BLOCKERASE 0x24 + +/* DOC_FLASHCOMMAND register commands */ +#define DOCG4_CMD_PAGE_READ 0x00 +#define DOC_CMD_ERASECYCLE2 0xd0 +#define DOCG4_CMD_FLUSH 0x70 +#define DOCG4_CMD_READ2 0x30 +#define DOC_CMD_PROG_BLOCK_ADDR 0x60 +#define DOCG4_CMD_PAGEWRITE 0x80 +#define DOC_CMD_PROG_CYCLE2 0x10 +#define DOC_CMD_RESET 0xff + +/* DOC_POWERMODE register bits */ +#define DOC_POWERDOWN_READY 0x80 + +/* DOC_FLASHCONTROL register bits */ +#define DOC_CTRL_CE 0x10 +#define DOC_CTRL_UNKNOWN 0x40 +#define DOC_CTRL_FLASHREADY 0x01 + +/* DOC_ECCCONF0 register bits */ +#define DOC_ECCCONF0_READ_MODE 0x8000 +#define DOC_ECCCONF0_UNKNOWN 0x2000 +#define DOC_ECCCONF0_ECC_ENABLE 0x1000 +#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff + +/* DOC_ECCCONF1 register bits */ +#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80 +#define DOC_ECCCONF1_ECC_ENABLE 0x07 +#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20 + +/* DOC_ASICMODE register bits */ +#define DOC_ASICMODE_RESET 0x00 +#define DOC_ASICMODE_NORMAL 0x01 +#define DOC_ASICMODE_POWERDOWN 0x02 +#define DOC_ASICMODE_MDWREN 0x04 +#define DOC_ASICMODE_BDETCT_RESET 0x08 +#define DOC_ASICMODE_RSTIN_RESET 0x10 +#define DOC_ASICMODE_RAM_WE 0x20 + +/* good status values read after read/write/erase operations */ +#define DOCG4_PROGSTATUS_GOOD 0x51 +#define DOCG4_PROGSTATUS_GOOD_2 0xe0 + +/* + * On read operations (page and oob-only), the first byte read from I/O reg is a + * status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read + * after reset only) or 0x51, so bit 1 is presumed to be an error indicator. + */ +#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */ + +/* anatomy of the device */ +#define DOCG4_CHIP_SIZE 0x8000000 +#define DOCG4_PAGE_SIZE 0x200 +#define DOCG4_PAGES_PER_BLOCK 0x200 +#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE) +#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE) +#define DOCG4_OOB_SIZE 0x10 +#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */ +#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */ +#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */ + +/* all but the last byte is included in ecc calculation */ +#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1) + +#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */ + +/* expected values from the ID registers */ +#define DOCG4_IDREG1_VALUE 0x0400 +#define DOCG4_IDREG2_VALUE 0xfbff + +/* primitive polynomial used to build the Galois field used by hw ecc gen */ +#define DOCG4_PRIMITIVE_POLY 0x4443 + +#define DOCG4_M 14 /* Galois field is of order 2^14 */ +#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */ + +#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */ + +/* + * Oob bytes 0 - 6 are available to the user. + * Byte 7 is hamming ecc for first 7 bytes. Bytes 8 - 14 are hw-generated ecc. + * Byte 15 (the last) is used by the driver as a "page written" flag. + */ +static struct nand_ecclayout docg4_oobinfo = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 7, + .oobfree = { {0, 7} } +}; + +/* + * The device has a nop register which M-Sys claims is for the purpose of + * inserting precise delays. But beware; at least some operations fail if the + * nop writes are replaced with a generic delay! + */ +static inline void write_nop(void __iomem *docptr) +{ + writew(0, docptr + DOC_NOP); +} + +static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd->priv; + uint16_t *p = (uint16_t *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + p[i] = readw(nand->IO_ADDR_R); +} + +static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd->priv; + uint16_t *p = (uint16_t *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + writew(p[i], nand->IO_ADDR_W); +} + +static int poll_status(struct docg4_priv *doc) +{ + /* + * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL + * register. Operations known to take a long time (e.g., block erase) + * should sleep for a while before calling this. + */ + + uint16_t flash_status; + unsigned int timeo; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s...\n", __func__); + + /* hardware quirk requires reading twice initially */ + flash_status = readw(docptr + DOC_FLASHCONTROL); + + timeo = 1000; + do { + cpu_relax(); + flash_status = readb(docptr + DOC_FLASHCONTROL); + } while (!(flash_status & DOC_CTRL_FLASHREADY) && --timeo); + + + if (!timeo) { + dev_err(doc->dev, "%s: timed out!\n", __func__); + return NAND_STATUS_FAIL; + } + + if (unlikely(timeo < 50)) + dev_warn(doc->dev, "%s: nearly timed out; %d remaining\n", + __func__, timeo); + + return 0; +} + + +static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand) +{ + + struct docg4_priv *doc = nand->priv; + int status = NAND_STATUS_WP; /* inverse logic?? */ + dev_dbg(doc->dev, "%s...\n", __func__); + + /* report any previously unreported error */ + if (doc->status) { + status |= doc->status; + doc->status = 0; + return status; + } + + status |= poll_status(doc); + return status; +} + +static void docg4_select_chip(struct mtd_info *mtd, int chip) +{ + /* + * Select among multiple cascaded chips ("floors"). Multiple floors are + * not yet supported, so the only valid non-negative value is 0. + */ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip); + + if (chip < 0) + return; /* deselected */ + + if (chip > 0) + dev_warn(doc->dev, "multiple floors currently unsupported\n"); + + writew(0, docptr + DOC_DEVICESELECT); +} + +static void reset(struct mtd_info *mtd) +{ + /* full device reset */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + write_nop(docptr); + + writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + + writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1); + + poll_status(doc); +} + +static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf) +{ + /* read the 7 hw-generated ecc bytes */ + + int i; + for (i = 0; i < 7; i++) { /* hw quirk; read twice */ + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + } +} + +static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + /* + * Called after a page read when hardware reports bitflips. + * Up to four bitflips can be corrected. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + int i, numerrs, errpos[4]; + const uint8_t blank_read_hwecc[8] = { + 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 }; + + read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */ + + /* check if read error is due to a blank page */ + if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7)) + return 0; /* yes */ + + /* skip additional check of "written flag" if ignore_badblocks */ + if (ignore_badblocks == false) { + + /* + * If the hw ecc bytes are not those of a blank page, there's + * still a chance that the page is blank, but was read with + * errors. Check the "written flag" in last oob byte, which + * is set to zero when a page is written. If more than half + * the bits are set, assume a blank page. Unfortunately, the + * bit flips(s) are not reported in stats. + */ + + if (doc->oob_buf[15]) { + int bit, numsetbits = 0; + unsigned long written_flag = doc->oob_buf[15]; + for_each_set_bit(bit, &written_flag, 8) + numsetbits++; + if (numsetbits > 4) { /* assume blank */ + dev_warn(doc->dev, + "error(s) in blank page " + "at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return 0; + } + } + } + + /* + * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch + * algorithm is used to decode this. However the hw operates on page + * data in a bit order that is the reverse of that of the bch alg, + * requiring that the bits be reversed on the result. Thanks to Ivan + * Djelic for his analysis! + */ + for (i = 0; i < 7; i++) + doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]); + + numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL, + doc->ecc_buf, NULL, errpos); + + if (numerrs == -EBADMSG) { + dev_warn(doc->dev, "uncorrectable errors at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return -EBADMSG; + } + + BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */ + + /* undo last step in BCH alg (modulo mirroring not needed) */ + for (i = 0; i < numerrs; i++) + errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7)); + + /* fix the errors */ + for (i = 0; i < numerrs; i++) { + + /* ignore if error within oob ecc bytes */ + if (errpos[i] > DOCG4_USERDATA_LEN * 8) + continue; + + /* if error within oob area preceeding ecc bytes... */ + if (errpos[i] > DOCG4_PAGE_SIZE * 8) + change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8, + (unsigned long *)doc->oob_buf); + + else /* error in page data */ + change_bit(errpos[i], (unsigned long *)buf); + } + + dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n", + numerrs, page * DOCG4_PAGE_SIZE); + + return numerrs; +} + +static uint8_t docg4_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + + dev_dbg(doc->dev, "%s\n", __func__); + + if (doc->last_command.command == NAND_CMD_STATUS) { + int status; + + /* + * Previous nand command was status request, so nand + * infrastructure code expects to read the status here. If an + * error occurred in a previous operation, report it. + */ + doc->last_command.command = 0; + + if (doc->status) { + status = doc->status; + doc->status = 0; + } + + /* why is NAND_STATUS_WP inverse logic?? */ + else + status = NAND_STATUS_WP | NAND_STATUS_READY; + + return status; + } + + dev_warn(doc->dev, "unexpectd call to read_byte()\n"); + + return 0; +} + +static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr) +{ + /* write the four address bytes packed in docg4_addr to the device */ + + void __iomem *docptr = doc->virtadr; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +} + +static int read_progstatus(struct docg4_priv *doc) +{ + /* + * This apparently checks the status of programming. Done after an + * erasure, and after page data is written. On error, the status is + * saved, to be later retrieved by the nand infrastructure code. + */ + void __iomem *docptr = doc->virtadr; + + /* status is read from the I/O reg */ + uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG); + + dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n", + __func__, status1, status2, status3); + + if (status1 != DOCG4_PROGSTATUS_GOOD + || status2 != DOCG4_PROGSTATUS_GOOD_2 + || status3 != DOCG4_PROGSTATUS_GOOD_2) { + doc->status = NAND_STATUS_FAIL; + dev_warn(doc->dev, "read_progstatus failed: " + "%02x, %02x, %02x\n", status1, status2, status3); + return -EIO; + } + return 0; +} + +static int pageprog(struct mtd_info *mtd) +{ + /* + * Final step in writing a page. Writes the contents of its + * internal buffer out to the flash array, or some such. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + int retval = 0; + + dev_dbg(doc->dev, "docg4: %s\n", __func__); + + writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + /* Just busy-wait; usleep_range() slows things down noticeably. */ + poll_status(doc); + + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + retval = read_progstatus(doc); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); + + return retval; +} + +static void sequence_reset(struct mtd_info *mtd) +{ + /* common starting sequence for all operations */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL); + writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); +} + +static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) +{ + /* first step in reading a page */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, + "docg4: %s: g4 page %08x\n", __func__, docg4_addr); + + sequence_reset(mtd); + + writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + write_addr(doc, docg4_addr); + + write_nop(docptr); + writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + poll_status(doc); +} + +static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) +{ + /* first step in writing a page */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, + "docg4: %s: g4 addr: %x\n", __func__, docg4_addr); + sequence_reset(mtd); + writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_addr(doc, docg4_addr); + write_nop(docptr); + write_nop(docptr); + poll_status(doc); +} + +static uint32_t mtd_to_docg4_address(int page, int column) +{ + /* + * Convert mtd address to format used by the device, 32 bit packed. + * + * Some notes on G4 addressing... The M-Sys documentation on this device + * claims that pages are 2K in length, and indeed, the format of the + * address used by the device reflects that. But within each page are + * four 512 byte "sub-pages", each with its own oob data that is + * read/written immediately after the 512 bytes of page data. This oob + * data contains the ecc bytes for the preceeding 512 bytes. + * + * Rather than tell the mtd nand infrastructure that page size is 2k, + * with four sub-pages each, we engage in a little subterfuge and tell + * the infrastructure code that pages are 512 bytes in size. This is + * done because during the course of reverse-engineering the device, I + * never observed an instance where an entire 2K "page" was read or + * written as a unit. Each "sub-page" is always addressed individually, + * its data read/written, and ecc handled before the next "sub-page" is + * addressed. + * + * This requires us to convert addresses passed by the mtd nand + * infrastructure code to those used by the device. + * + * The address that is written to the device consists of four bytes: the + * first two are the 2k page number, and the second is the index into + * the page. The index is in terms of 16-bit half-words and includes + * the preceeding oob data, so e.g., the index into the second + * "sub-page" is 0x108, and the full device address of the start of mtd + * page 0x201 is 0x00800108. + */ + int g4_page = page / 4; /* device's 2K page */ + int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */ + return (g4_page << 16) | g4_index; /* pack */ +} + +static void docg4_command(struct mtd_info *mtd, unsigned command, int column, + int page_addr) +{ + /* handle standard nand commands */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + uint32_t g4_addr = mtd_to_docg4_address(page_addr, column); + + dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n", + __func__, command, page_addr, column); + + /* + * Save the command and its arguments. This enables emulation of + * standard flash devices, and also some optimizations. + */ + doc->last_command.command = command; + doc->last_command.column = column; + doc->last_command.page = page_addr; + + switch (command) { + + case NAND_CMD_RESET: + reset(mtd); + break; + + case NAND_CMD_READ0: + read_page_prologue(mtd, g4_addr); + break; + + case NAND_CMD_STATUS: + /* next call to read_byte() will expect a status */ + break; + + case NAND_CMD_SEQIN: + write_page_prologue(mtd, g4_addr); + + /* hack for deferred write of oob bytes */ + if (doc->oob_page == page_addr) + memcpy(nand->oob_poi, doc->oob_buf, 16); + break; + + case NAND_CMD_PAGEPROG: + pageprog(mtd); + break; + + /* we don't expect these, based on review of nand_base.c */ + case NAND_CMD_READOOB: + case NAND_CMD_READID: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + dev_warn(doc->dev, "docg4_command: " + "unexpected nand command 0x%x\n", command); + break; + + } +} + +static int read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page, bool use_ecc) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t status, edc_err, *buf16; + + dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); + + writew(DOC_ECCCONF0_READ_MODE | + DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is page data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + dev_err(doc->dev, + "docg4_read_page: bad status: 0x%02x\n", status); + writew(0, docptr + DOC_DATAEND); + return -EIO; + } + + dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); + + docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */ + + /* + * Diskonchips read oob immediately after a page read. Mtd + * infrastructure issues a separate command for reading oob after the + * page is read. So we save the oob bytes in a local buffer and just + * copy it if the next command reads oob from the same page. + */ + + /* first 14 oob bytes read from I/O reg */ + docg4_read_buf(mtd, doc->oob_buf, 14); + + /* last 2 read from another reg */ + buf16 = (uint16_t *)(doc->oob_buf + 14); + *buf16 = readw(docptr + DOCG4_MYSTERY_REG); + + write_nop(docptr); + + if (likely(use_ecc == true)) { + + /* read the register that tells us if bitflip(s) detected */ + edc_err = readw(docptr + DOC_ECCCONF1); + edc_err = readw(docptr + DOC_ECCCONF1); + dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err); + + /* If bitflips are reported, attempt to correct with ecc */ + if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { + int bits_corrected = correct_data(mtd, buf, page); + if (bits_corrected == -EBADMSG) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += bits_corrected; + } + } + + writew(0, docptr + DOC_DATAEND); + return 0; +} + + +static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page) +{ + return read_page(mtd, nand, buf, page, false); +} + +static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page) +{ + return read_page(mtd, nand, buf, page, true); +} + +static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page, int sndcmd) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t status; + + dev_dbg(doc->dev, "%s: page %x\n", __func__, page); + + /* + * Oob bytes are read as part of a normal page read. If the previous + * nand command was a read of the page whose oob is now being read, just + * copy the oob bytes that we saved in a local buffer and avoid a + * separate oob read. + */ + if (doc->last_command.command == NAND_CMD_READ0 && + doc->last_command.page == page) { + memcpy(nand->oob_poi, doc->oob_buf, 16); + return 0; + } + + /* + * Separate read of oob data only. + */ + docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page); + + writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is oob data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + dev_warn(doc->dev, + "docg4_read_oob failed: status = 0x%02x\n", status); + return -EIO; + } + + dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); + + docg4_read_buf(mtd, nand->oob_poi, 16); + + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + + return 0; +} + +static void docg4_erase_block(struct mtd_info *mtd, int page) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t g4_page; + + dev_dbg(doc->dev, "%s: page %04x\n", __func__, page); + + sequence_reset(mtd); + + writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + /* only 2 bytes of address are written to specify erase block */ + g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */ + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + g4_page >>= 8; + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + write_nop(docptr); + + /* start the erasure */ + writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + usleep_range(500, 1000); /* erasure is long; take a snooze */ + poll_status(doc); + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + read_progstatus(doc); + + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); +} + +static void write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, bool use_ecc) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint8_t ecc_buf[8]; + + dev_dbg(doc->dev, "%s...\n", __func__); + + writew(DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + + /* write the page data */ + docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE); + + /* oob bytes 0 through 5 are written to I/O reg */ + docg4_write_buf16(mtd, nand->oob_poi, 6); + + /* oob byte 6 written to a separate reg */ + writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7); + + write_nop(docptr); + write_nop(docptr); + + /* write hw-generated ecc bytes to oob */ + if (likely(use_ecc == true)) { + /* oob byte 7 is hamming code */ + uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY); + hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */ + writew(hamming, docptr + DOCG4_OOB_6_7); + write_nop(docptr); + + /* read the 7 bch bytes from ecc regs */ + read_hw_ecc(docptr, ecc_buf); + ecc_buf[7] = 0; /* clear the "page written" flag */ + } + + /* write user-supplied bytes to oob */ + else { + writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7); + write_nop(docptr); + memcpy(ecc_buf, &nand->oob_poi[8], 8); + } + + docg4_write_buf16(mtd, ecc_buf, 8); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); +} + +static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf) +{ + return write_page(mtd, nand, buf, false); +} + +static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf) +{ + return write_page(mtd, nand, buf, true); +} + +static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + /* + * Writing oob-only is not really supported, because MLC nand must write + * oob bytes at the same time as page data. Nonetheless, we save the + * oob buffer contents here, and then write it along with the page data + * if the same page is subsequently written. This allows user space + * utilities that write the oob data prior to the page data to work + * (e.g., nandwrite). The disdvantage is that, if the intention was to + * write oob only, the operation is quietly ignored. Also, oob can get + * corrupted if two concurrent processes are running nandwrite. + */ + + /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */ + struct docg4_priv *doc = nand->priv; + doc->oob_page = page; + memcpy(doc->oob_buf, nand->oob_poi, 16); + return 0; +} + +static int __init read_factory_bbt(struct mtd_info *mtd) +{ + /* + * The device contains a read-only factory bad block table. Read it and + * update the memory-based bbt accordingly. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); + uint8_t *buf; + int i, block, status; + + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + read_page_prologue(mtd, g4_addr); + status = docg4_read_page(mtd, nand, buf, DOCG4_FACTORY_BBT_PAGE); + if (status) + goto exit; + + /* + * If no memory-based bbt was created, exit. This will happen if module + * parameter ignore_badblocks is set. Then why even call this function? + * For an unknown reason, block erase always fails if it's the first + * operation after device power-up. The above read ensures it never is. + * Ugly, I know. + */ + if (nand->bbt == NULL) /* no memory-based bbt */ + goto exit; + + /* + * Parse factory bbt and update memory-based bbt. Factory bbt format is + * simple: one bit per block, block numbers increase left to right (msb + * to lsb). Bit clear means bad block. + */ + for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) { + int bitnum; + unsigned long bits = ~buf[i]; + for_each_set_bit(bitnum, &bits, 8) { + int badblock = block + 7 - bitnum; + nand->bbt[badblock / 4] |= + 0x03 << ((badblock % 4) * 2); + mtd->ecc_stats.badblocks++; + dev_notice(doc->dev, "factory-marked bad block: %d\n", + badblock); + } + } + exit: + kfree(buf); + return status; +} + +static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + /* + * Mark a block as bad. Bad blocks are marked in the oob area of the + * first page of the block. The default scan_bbt() in the nand + * infrastructure code works fine for building the memory-based bbt + * during initialization, as does the nand infrastructure function that + * checks if a block is bad by reading the bbt. This function replaces + * the nand default because writes to oob-only are not supported. + */ + + int ret, i; + uint8_t *buf; + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + struct nand_bbt_descr *bbtd = nand->badblock_pattern; + int block = (int)(ofs >> nand->bbt_erase_shift); + int page = (int)(ofs >> nand->page_shift); + uint32_t g4_addr = mtd_to_docg4_address(page, 0); + + dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs); + + if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1))) + dev_warn(doc->dev, "%s: ofs %llx not start of block!\n", + __func__, ofs); + + /* allocate blank buffer for page data */ + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + /* update bbt in memory */ + nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2); + + /* write bit-wise negation of pattern to oob buffer */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + for (i = 0; i < bbtd->len; i++) + nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i]; + + /* write first page of block */ + write_page_prologue(mtd, g4_addr); + docg4_write_page(mtd, nand, buf); + ret = pageprog(mtd); + if (!ret) + mtd->ecc_stats.badblocks++; + + kfree(buf); + + return ret; +} + +static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + /* only called when module_param ignore_badblocks is set */ + return 0; +} + +static int docg4_suspend(struct platform_device *pdev, pm_message_t state) +{ + /* + * Put the device into "deep power-down" mode. Note that CE# must be + * deasserted for this to take effect. The xscale, e.g., can be + * configured to float this signal when the processor enters power-down, + * and a suitable pull-up ensures its deassertion. + */ + + int i; + uint8_t pwr_down; + struct docg4_priv *doc = platform_get_drvdata(pdev); + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s...\n", __func__); + + /* poll the register that tells us we're ready to go to sleep */ + for (i = 0; i < 10; i++) { + pwr_down = readb(docptr + DOC_POWERMODE); + if (pwr_down & DOC_POWERDOWN_READY) + break; + usleep_range(1000, 4000); + } + + if (pwr_down & DOC_POWERDOWN_READY) { + dev_err(doc->dev, "suspend failed; " + "timeout polling DOC_POWERDOWN_READY\n"); + return -EIO; + } + + writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + + write_nop(docptr); + + return 0; +} + +static int docg4_resume(struct platform_device *pdev) +{ + + /* + * Exit power-down. Twelve consecutive reads of the address below + * accomplishes this, assuming CE# has been asserted. + */ + + struct docg4_priv *doc = platform_get_drvdata(pdev); + void __iomem *docptr = doc->virtadr; + int i; + + dev_dbg(doc->dev, "%s...\n", __func__); + + for (i = 0; i < 12; i++) + readb(docptr + 0x1fff); + + return 0; +} + +static void __init init_mtd_structs(struct mtd_info *mtd) +{ + /* initialize mtd and nand data structures */ + + /* + * Note that some of the following initializations are not usually + * required within a nand driver because they are performed by the nand + * infrastructure code as part of nand_scan(). In this case they need + * to be initialized here because we skip call to nand_scan_ident() (the + * first half of nand_scan()). The call to nand_scan_ident() is skipped + * because for this device the chip id is not read in the manner of a + * standard nand device. Unfortunately, nand_scan_ident() does other + * things as well, such as call nand_set_defaults(). + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + + mtd->size = DOCG4_CHIP_SIZE; + mtd->name = "Msys_Diskonchip_G4"; + mtd->writesize = DOCG4_PAGE_SIZE; + mtd->erasesize = DOCG4_BLOCK_SIZE; + mtd->oobsize = DOCG4_OOB_SIZE; + nand->chipsize = DOCG4_CHIP_SIZE; + nand->chip_shift = DOCG4_CHIP_SHIFT; + nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT; + nand->chip_delay = 20; + nand->page_shift = DOCG4_PAGE_SHIFT; + nand->pagemask = 0x3ffff; + nand->badblockpos = NAND_LARGE_BADBLOCK_POS; + nand->badblockbits = 8; + nand->ecc.layout = &docg4_oobinfo; + nand->ecc.mode = NAND_ECC_HW_SYNDROME; + nand->ecc.size = DOCG4_PAGE_SIZE; + nand->ecc.prepad = 8; + nand->ecc.bytes = 8; + nand->ecc.strength = DOCG4_T; + nand->options = + NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR; + nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; + nand->controller = &nand->hwcontrol; + spin_lock_init(&nand->controller->lock); + init_waitqueue_head(&nand->controller->wq); + + /* methods */ + nand->cmdfunc = docg4_command; + nand->waitfunc = docg4_wait; + nand->select_chip = docg4_select_chip; + nand->read_byte = docg4_read_byte; + nand->block_markbad = docg4_block_markbad; + nand->read_buf = docg4_read_buf; + nand->write_buf = docg4_write_buf16; + nand->scan_bbt = nand_default_bbt; + nand->erase_cmd = docg4_erase_block; + nand->ecc.read_page = docg4_read_page; + nand->ecc.write_page = docg4_write_page; + nand->ecc.read_page_raw = docg4_read_page_raw; + nand->ecc.write_page_raw = docg4_write_page_raw; + nand->ecc.read_oob = docg4_read_oob; + nand->ecc.write_oob = docg4_write_oob; + + /* + * The way the nand infrastructure code is written, a memory-based bbt + * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt, + * nand->block_bad() is used. So when ignoring bad blocks, we skip the + * scan and define a dummy block_bad() which always returns 0. + */ + if (ignore_badblocks) { + nand->options |= NAND_SKIP_BBTSCAN; + nand->block_bad = docg4_block_neverbad; + } + +} + +static int __init read_id_reg(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t id1, id2; + + /* check for presence of g4 chip by reading id registers */ + id1 = readw(docptr + DOC_CHIPID); + id1 = readw(docptr + DOCG4_MYSTERY_REG); + id2 = readw(docptr + DOC_CHIPID_INV); + id2 = readw(docptr + DOCG4_MYSTERY_REG); + + if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) { + dev_info(doc->dev, + "NAND device: 128MiB Diskonchip G4 detected\n"); + return 0; + } + + return -ENODEV; +} + +static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL }; + +static int __init probe_docg4(struct platform_device *pdev) +{ + struct mtd_info *mtd; + struct nand_chip *nand; + void __iomem *virtadr; + struct docg4_priv *doc; + int len, retval; + struct resource *r; + struct device *dev = &pdev->dev; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(dev, "no io memory resource defined!\n"); + return -ENODEV; + } + + virtadr = ioremap(r->start, resource_size(r)); + if (!virtadr) { + dev_err(dev, "Diskonchip ioremap failed: %pR\n", r); + return -EIO; + } + + len = sizeof(struct mtd_info) + sizeof(struct nand_chip) + + sizeof(struct docg4_priv); + mtd = kzalloc(len, GFP_KERNEL); + if (mtd == NULL) { + retval = -ENOMEM; + goto fail; + } + nand = (struct nand_chip *) (mtd + 1); + doc = (struct docg4_priv *) (nand + 1); + mtd->priv = nand; + nand->priv = doc; + mtd->owner = THIS_MODULE; + doc->virtadr = virtadr; + doc->dev = dev; + + init_mtd_structs(mtd); + + /* initialize kernel bch algorithm */ + doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); + if (doc->bch == NULL) { + retval = -EINVAL; + goto fail; + } + + platform_set_drvdata(pdev, doc); + + reset(mtd); + retval = read_id_reg(mtd); + if (retval == -ENODEV) { + dev_warn(dev, "No diskonchip G4 device found.\n"); + goto fail; + } + + retval = nand_scan_tail(mtd); + if (retval) + goto fail; + + retval = read_factory_bbt(mtd); + if (retval) + goto fail; + + retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); + if (retval) + goto fail; + + doc->mtd = mtd; + return 0; + + fail: + iounmap(virtadr); + if (mtd) { + /* re-declarations avoid compiler warning */ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + nand_release(mtd); /* deletes partitions and mtd devices */ + platform_set_drvdata(pdev, NULL); + free_bch(doc->bch); + kfree(mtd); + } + + return retval; +} + +static int __exit cleanup_docg4(struct platform_device *pdev) +{ + struct docg4_priv *doc = platform_get_drvdata(pdev); + nand_release(doc->mtd); + platform_set_drvdata(pdev, NULL); + free_bch(doc->bch); + kfree(doc->mtd); + iounmap(doc->virtadr); + return 0; +} + +static struct platform_driver docg4_driver = { + .driver = { + .name = "docg4", + .owner = THIS_MODULE, + }, + .suspend = docg4_suspend, + .resume = docg4_resume, + .remove = __exit_p(cleanup_docg4), +}; + +static int __init docg4_init(void) +{ + return platform_driver_probe(&docg4_driver, probe_docg4); +} + +static void __exit docg4_exit(void) +{ + platform_driver_unregister(&docg4_driver); +} + +module_init(docg4_init); +module_exit(docg4_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Dunn"); +MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver"); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 7195ee6efe1..80b5264f0a3 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -813,6 +813,12 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0; chip->ecc.size = 512; chip->ecc.bytes = 3; + chip->ecc.strength = 1; + /* + * FIXME: can hardware ecc correct 4 bitflips if page size is + * 2k? Then does hardware report number of corrections for this + * case? If so, ecc_stats reporting needs to be fixed as well. + */ } else { /* otherwise fall back to default software ECC */ chip->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c new file mode 100644 index 00000000000..c30ac7b83d2 --- /dev/null +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -0,0 +1,1072 @@ +/* + * Freescale Integrated Flash Controller NAND driver + * + * Copyright 2011-2012 Freescale Semiconductor, Inc + * + * Author: Dipen Dudhat <Dipen.Dudhat@freescale.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/nand_ecc.h> +#include <asm/fsl_ifc.h> + +#define ERR_BYTE 0xFF /* Value returned for read + bytes when read failed */ +#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait + for IFC NAND Machine */ + +struct fsl_ifc_ctrl; + +/* mtd information per set */ +struct fsl_ifc_mtd { + struct mtd_info mtd; + struct nand_chip chip; + struct fsl_ifc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ + u8 __iomem *vbase; /* Chip select base virtual address */ +}; + +/* overview of the fsl ifc controller */ +struct fsl_ifc_nand_ctrl { + struct nand_hw_control controller; + struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT]; + + u8 __iomem *addr; /* Address of assigned IFC buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes;/* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int oob; /* Non zero if operating on OOB data */ + unsigned int eccread; /* Non zero for a full-page ECC read */ + unsigned int counter; /* counter for the initializations */ +}; + +static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; + +/* 512-byte page with 4-bit ECC, 8-bit */ +static struct nand_ecclayout oob_512_8bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {0, 5}, {6, 2} }, +}; + +/* 512-byte page with 4-bit ECC, 16-bit */ +static struct nand_ecclayout oob_512_16bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {2, 6}, }, +}; + +/* 2048-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_2048_ecc4 = { + .eccbytes = 32, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + }, + .oobfree = { {2, 6}, {40, 24} }, +}; + +/* 4096-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_4096_ecc4 = { + .eccbytes = 64, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + }, + .oobfree = { {2, 6}, {72, 56} }, +}; + +/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_4096_ecc8 = { + .eccbytes = 128, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobfree = { {2, 6}, {136, 82} }, +}; + + +/* + * Generic flash bbt descriptors + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 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, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .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, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* + * Set up the IFC hardware block and page address fields, and the ifc nand + * structure addr field to point to the correct IFC buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + int buf_num; + + ifc_nand_ctrl->page = page_addr; + /* Program ROW0/COL0 */ + out_be32(&ifc->ifc_nand.row0, page_addr); + out_be32(&ifc->ifc_nand.col0, (oob ? IFC_NAND_COL_MS : 0) | column); + + buf_num = page_addr & priv->bufnum_mask; + + ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); + ifc_nand_ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ifc_nand_ctrl->index += mtd->writesize; +} + +static int is_blank(struct mtd_info *mtd, unsigned int bufnum) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2); + u32 __iomem *mainarea = (u32 *)addr; + u8 __iomem *oob = addr + mtd->writesize; + int i; + + for (i = 0; i < mtd->writesize / 4; i++) { + if (__raw_readl(&mainarea[i]) != 0xffffffff) + return 0; + } + + for (i = 0; i < chip->ecc.layout->eccbytes; i++) { + int pos = chip->ecc.layout->eccpos[i]; + + if (__raw_readb(&oob[pos]) != 0xff) + return 0; + } + + return 1; +} + +/* returns nonzero if entire page is blank */ +static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, + u32 *eccstat, unsigned int bufnum) +{ + u32 reg = eccstat[bufnum / 4]; + int errors; + + errors = (reg >> ((3 - bufnum % 4) * 8)) & 15; + + return errors; +} + +/* + * execute IFC NAND command and wait for it to complete + */ +static void fsl_ifc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + u32 eccstat[4]; + int i; + + /* set the chip select for NAND Transaction */ + out_be32(&ifc->ifc_nand.nand_csel, priv->bank << IFC_NAND_CSEL_SHIFT); + + dev_vdbg(priv->dev, + "%s: fir0=%08x fcr0=%08x\n", + __func__, + in_be32(&ifc->ifc_nand.nand_fir0), + in_be32(&ifc->ifc_nand.nand_fcr0)); + + ctrl->nand_stat = 0; + + /* start read/write seq */ + out_be32(&ifc->ifc_nand.nandseq_strt, IFC_NAND_SEQ_STRT_FIR_STRT); + + /* wait for command complete flag or timeout */ + wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, + IFC_TIMEOUT_MSECS * HZ/1000); + + /* ctrl->nand_stat will be updated from IRQ context */ + if (!ctrl->nand_stat) + dev_err(priv->dev, "Controller is not responding\n"); + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER) + dev_err(priv->dev, "NAND Flash Timeout Error\n"); + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) + dev_err(priv->dev, "NAND Flash Write Protect Error\n"); + + if (nctrl->eccread) { + int errors; + int bufnum = nctrl->page & priv->bufnum_mask; + int sector = bufnum * chip->ecc.steps; + int sector_end = sector + chip->ecc.steps - 1; + + for (i = sector / 4; i <= sector_end / 4; i++) + eccstat[i] = in_be32(&ifc->ifc_nand.nand_eccstat[i]); + + for (i = sector; i <= sector_end; i++) { + errors = check_read_ecc(mtd, ctrl, eccstat, i); + + if (errors == 15) { + /* + * Uncorrectable error. + * OK only if the whole page is blank. + * + * We disable ECCER reporting due to... + * erratum IFC-A002770 -- so report it now if we + * see an uncorrectable error in ECCSTAT. + */ + if (!is_blank(mtd, bufnum)) + ctrl->nand_stat |= + IFC_NAND_EVTER_STAT_ECCER; + break; + } + + mtd->ecc_stats.corrected += errors; + } + + nctrl->eccread = 0; + } +} + +static void fsl_ifc_do_read(struct nand_chip *chip, + int oob, + struct mtd_info *mtd) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + + /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ + if (mtd->writesize > 512) { + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + + out_be32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); + } else { + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + + if (oob) + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT); + else + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); + } +} + +/* cmdfunc send commands to the IFC NAND Machine */ +static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + + /* clear the read buffer */ + ifc_nand_ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ifc_nand_ctrl->index = 0; + + switch (command) { + /* READ0 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ0: + out_be32(&ifc->ifc_nand.nand_fbcr, 0); + set_addr(mtd, 0, page_addr, 0); + + ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ifc_nand_ctrl->index += column; + + if (chip->ecc.mode == NAND_ECC_HW) + ifc_nand_ctrl->eccread = 1; + + fsl_ifc_do_read(chip, 0, mtd); + fsl_ifc_run_command(mtd); + return; + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + out_be32(&ifc->ifc_nand.nand_fbcr, mtd->oobsize - column); + set_addr(mtd, column, page_addr, 1); + + ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_ifc_do_read(chip, 1, mtd); + fsl_ifc_run_command(mtd); + + return; + + /* READID must read all 8 possible bytes */ + case NAND_CMD_READID: + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT); + /* 8 bytes for manuf, device and exts */ + out_be32(&ifc->ifc_nand.nand_fbcr, 8); + ifc_nand_ctrl->read_bytes = 8; + + set_addr(mtd, 0, 0, 0); + fsl_ifc_run_command(mtd); + return; + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT)); + + out_be32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT)); + + out_be32(&ifc->ifc_nand.nand_fbcr, 0); + ifc_nand_ctrl->read_bytes = 0; + fsl_ifc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + u32 nand_fcr0; + ifc_nand_ctrl->column = column; + ifc_nand_ctrl->oob = 0; + + if (mtd->writesize > 512) { + nand_fcr0 = + (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD1_SHIFT); + + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_CW1 << IFC_NAND_FIR0_OP4_SHIFT)); + } else { + nand_fcr0 = ((NAND_CMD_PAGEPROG << + IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_SEQIN << + IFC_NAND_FCR0_CMD2_SHIFT)); + + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fir1, + (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT)); + + if (column >= mtd->writesize) + nand_fcr0 |= + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; + else + nand_fcr0 |= + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; + } + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + ifc_nand_ctrl->oob = 1; + } + out_be32(&ifc->ifc_nand.nand_fcr0, nand_fcr0); + set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: { + if (ifc_nand_ctrl->oob) { + out_be32(&ifc->ifc_nand.nand_fbcr, + ifc_nand_ctrl->index - ifc_nand_ctrl->column); + } else { + out_be32(&ifc->ifc_nand.nand_fbcr, 0); + } + + fsl_ifc_run_command(mtd); + return; + } + + case NAND_CMD_STATUS: + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT); + out_be32(&ifc->ifc_nand.nand_fbcr, 1); + set_addr(mtd, 0, 0, 0); + ifc_nand_ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + /* + * The chip always seems to report that it is + * write-protected, even when it is not. + */ + setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP); + return; + + case NAND_CMD_RESET: + out_be32(&ifc->ifc_nand.nand_fir0, + IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT); + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT); + fsl_ifc_run_command(mtd); + return; + + default: + dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n", + __func__, command); + } +} + +static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) +{ + /* The hardware does not seem to support multiple + * chips per bank. + */ +} + +/* + * Write buf to the IFC NAND Controller Data Buffer + */ +static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len <= 0) { + dev_err(priv->dev, "%s: len %d bytes", __func__, len); + return; + } + + if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) { + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %u available)\n", + __func__, len, bufsize - ifc_nand_ctrl->index); + len = bufsize - ifc_nand_ctrl->index; + } + + memcpy_toio(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index], buf, len); + ifc_nand_ctrl->index += len; +} + +/* + * Read a byte from either the IFC hardware buffer + * read function for 8-bit buswidth + */ +static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) + return in_8(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index++]); + + dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read two bytes from the IFC hardware buffer + * read function for 16-bit buswith + */ +static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + uint16_t data; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { + data = in_be16((uint16_t *)&ifc_nand_ctrl-> + addr[ifc_nand_ctrl->index]); + ifc_nand_ctrl->index += 2; + return (uint8_t) data; + } + + dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read from the IFC Controller Data Buffer + */ +static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + int avail; + + if (len < 0) { + dev_err(priv->dev, "%s: len %d bytes", __func__, len); + return; + } + + avail = min((unsigned int)len, + ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index); + memcpy_fromio(buf, &ifc_nand_ctrl->addr[ifc_nand_ctrl->index], avail); + ifc_nand_ctrl->index += avail; + + if (len > avail) + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %d available)\n", + __func__, len, avail); +} + +/* + * Verify buffer against the IFC Controller Data Buffer + */ +static int fsl_ifc_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; + int i; + + if (len < 0) { + dev_err(priv->dev, "%s: write_buf of %d bytes", __func__, len); + return -EINVAL; + } + + if ((unsigned int)len > nctrl->read_bytes - nctrl->index) { + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %u available)\n", + __func__, len, nctrl->read_bytes - nctrl->index); + + nctrl->index = nctrl->read_bytes; + return -EINVAL; + } + + for (i = 0; i < len; i++) + if (in_8(&nctrl->addr[nctrl->index + i]) != buf[i]) + break; + + nctrl->index += len; + + if (i != len) + return -EIO; + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + return -EIO; + + return 0; +} + +/* + * This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + u32 nand_fsr; + + /* Use READ_STATUS command, but wait for the device to be ready */ + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fcr0, NAND_CMD_STATUS << + IFC_NAND_FCR0_CMD0_SHIFT); + out_be32(&ifc->ifc_nand.nand_fbcr, 1); + set_addr(mtd, 0, 0, 0); + ifc_nand_ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + nand_fsr = in_be32(&ifc->ifc_nand.nand_fsr); + + /* + * The chip always seems to report that it is + * write-protected, even when it is not. + */ + return nand_fsr | NAND_STATUS_WP; +} + +static int fsl_ifc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + + fsl_ifc_read_buf(mtd, buf, mtd->writesize); + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) + dev_err(priv->dev, "NAND Flash ECC Uncorrectable Error\n"); + + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + mtd->ecc_stats.failed++; + + return 0; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static void fsl_ifc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf) +{ + fsl_ifc_write_buf(mtd, buf, mtd->writesize); + fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + +static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + + dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, + chip->numchips); + dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__, + chip->chipsize); + dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__, + chip->pagemask); + dev_dbg(priv->dev, "%s: nand->chip_delay = %d\n", __func__, + chip->chip_delay); + dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__, + chip->badblockpos); + dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__, + chip->chip_shift); + dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__, + chip->page_shift); + dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, + chip->phys_erase_shift); + dev_dbg(priv->dev, "%s: nand->ecclayout = %p\n", __func__, + chip->ecclayout); + dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__, + chip->ecc.mode); + dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, + chip->ecc.steps); + dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__, + chip->ecc.bytes); + dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__, + chip->ecc.total); + dev_dbg(priv->dev, "%s: nand->ecc.layout = %p\n", __func__, + chip->ecc.layout); + dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags); + dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size); + dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__, + mtd->erasesize); + dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__, + mtd->writesize); + dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__, + mtd->oobsize); + + return 0; +} + +static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) +{ + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct nand_chip *chip = &priv->chip; + struct nand_ecclayout *layout; + u32 csor; + + /* Fill in fsl_ifc_mtd structure */ + priv->mtd.priv = chip; + priv->mtd.owner = THIS_MODULE; + + /* fill in nand_chip structure */ + /* set up function call table */ + if ((in_be32(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) + chip->read_byte = fsl_ifc_read_byte16; + else + chip->read_byte = fsl_ifc_read_byte; + + chip->write_buf = fsl_ifc_write_buf; + chip->read_buf = fsl_ifc_read_buf; + chip->verify_buf = fsl_ifc_verify_buf; + chip->select_chip = fsl_ifc_select_chip; + chip->cmdfunc = fsl_ifc_cmdfunc; + chip->waitfunc = fsl_ifc_wait; + + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + + out_be32(&ifc->ifc_nand.ncfgr, 0x0); + + /* set up nand options */ + chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->bbt_options = NAND_BBT_USE_FLASH; + + + if (in_be32(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { + chip->read_byte = fsl_ifc_read_byte16; + chip->options |= NAND_BUSWIDTH_16; + } else { + chip->read_byte = fsl_ifc_read_byte; + } + + chip->controller = &ifc_nand_ctrl->controller; + chip->priv = priv; + + chip->ecc.read_page = fsl_ifc_read_page; + chip->ecc.write_page = fsl_ifc_write_page; + + csor = in_be32(&ifc->csor_cs[priv->bank].csor); + + /* Hardware generates ECC per 512 Bytes */ + chip->ecc.size = 512; + chip->ecc.bytes = 8; + + switch (csor & CSOR_NAND_PGS_MASK) { + case CSOR_NAND_PGS_512: + if (chip->options & NAND_BUSWIDTH_16) { + layout = &oob_512_16bit_ecc4; + } else { + layout = &oob_512_8bit_ecc4; + + /* Avoid conflict with bad block marker */ + bbt_main_descr.offs = 0; + bbt_mirror_descr.offs = 0; + } + + priv->bufnum_mask = 15; + break; + + case CSOR_NAND_PGS_2K: + layout = &oob_2048_ecc4; + priv->bufnum_mask = 3; + break; + + case CSOR_NAND_PGS_4K: + if ((csor & CSOR_NAND_ECC_MODE_MASK) == + CSOR_NAND_ECC_MODE_4) { + layout = &oob_4096_ecc4; + } else { + layout = &oob_4096_ecc8; + chip->ecc.bytes = 16; + } + + priv->bufnum_mask = 1; + break; + + default: + dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); + return -ENODEV; + } + + /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ + if (csor & CSOR_NAND_ECC_DEC_EN) { + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.layout = layout; + } else { + chip->ecc.mode = NAND_ECC_SOFT; + } + + return 0; +} + +static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) +{ + nand_release(&priv->mtd); + + kfree(priv->mtd.name); + + if (priv->vbase) + iounmap(priv->vbase); + + ifc_nand_ctrl->chips[priv->bank] = NULL; + dev_set_drvdata(priv->dev, NULL); + kfree(priv); + + return 0; +} + +static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, + phys_addr_t addr) +{ + u32 cspr = in_be32(&ifc->cspr_cs[bank].cspr); + + if (!(cspr & CSPR_V)) + return 0; + if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND) + return 0; + + return (cspr & CSPR_BA) == convert_ifc_address(addr); +} + +static DEFINE_MUTEX(fsl_ifc_nand_mutex); + +static int __devinit fsl_ifc_nand_probe(struct platform_device *dev) +{ + struct fsl_ifc_regs __iomem *ifc; + struct fsl_ifc_mtd *priv; + struct resource res; + static const char *part_probe_types[] + = { "cmdlinepart", "RedBoot", "ofpart", NULL }; + int ret; + int bank; + struct device_node *node = dev->dev.of_node; + struct mtd_part_parser_data ppdata; + + ppdata.of_node = dev->dev.of_node; + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) + return -ENODEV; + ifc = fsl_ifc_ctrl_dev->regs; + + /* get, allocate and map the memory resource */ + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(&dev->dev, "%s: failed to get resource\n", __func__); + return ret; + } + + /* find which chip select it is connected to */ + for (bank = 0; bank < FSL_IFC_BANK_COUNT; bank++) { + if (match_bank(ifc, bank, res.start)) + break; + } + + if (bank >= FSL_IFC_BANK_COUNT) { + dev_err(&dev->dev, "%s: address did not match any chip selects\n", + __func__); + return -ENODEV; + } + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_lock(&fsl_ifc_nand_mutex); + if (!fsl_ifc_ctrl_dev->nand) { + ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL); + if (!ifc_nand_ctrl) { + dev_err(&dev->dev, "failed to allocate memory\n"); + mutex_unlock(&fsl_ifc_nand_mutex); + return -ENOMEM; + } + + ifc_nand_ctrl->read_bytes = 0; + ifc_nand_ctrl->index = 0; + ifc_nand_ctrl->addr = NULL; + fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; + + spin_lock_init(&ifc_nand_ctrl->controller.lock); + init_waitqueue_head(&ifc_nand_ctrl->controller.wq); + } else { + ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; + } + mutex_unlock(&fsl_ifc_nand_mutex); + + ifc_nand_ctrl->chips[bank] = priv; + priv->bank = bank; + priv->ctrl = fsl_ifc_ctrl_dev; + priv->dev = &dev->dev; + + priv->vbase = ioremap(res.start, resource_size(&res)); + if (!priv->vbase) { + dev_err(priv->dev, "%s: failed to map chip region\n", __func__); + ret = -ENOMEM; + goto err; + } + + dev_set_drvdata(priv->dev, priv); + + out_be32(&ifc->ifc_nand.nand_evter_en, + IFC_NAND_EVTER_EN_OPC_EN | + IFC_NAND_EVTER_EN_FTOER_EN | + IFC_NAND_EVTER_EN_WPER_EN); + + /* enable NAND Machine Interrupts */ + out_be32(&ifc->ifc_nand.nand_evter_intr_en, + IFC_NAND_EVTER_INTR_OPCIR_EN | + IFC_NAND_EVTER_INTR_FTOERIR_EN | + IFC_NAND_EVTER_INTR_WPERIR_EN); + + priv->mtd.name = kasprintf(GFP_KERNEL, "%x.flash", (unsigned)res.start); + if (!priv->mtd.name) { + ret = -ENOMEM; + goto err; + } + + ret = fsl_ifc_chip_init(priv); + if (ret) + goto err; + + ret = nand_scan_ident(&priv->mtd, 1, NULL); + if (ret) + goto err; + + ret = fsl_ifc_chip_init_tail(&priv->mtd); + if (ret) + goto err; + + ret = nand_scan_tail(&priv->mtd); + if (ret) + goto err; + + /* First look for RedBoot table or partitions on the command + * line, these take precedence over device tree information */ + mtd_device_parse_register(&priv->mtd, part_probe_types, &ppdata, + NULL, 0); + + dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", + (unsigned long long)res.start, priv->bank); + return 0; + +err: + fsl_ifc_chip_remove(priv); + return ret; +} + +static int fsl_ifc_nand_remove(struct platform_device *dev) +{ + struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); + + fsl_ifc_chip_remove(priv); + + mutex_lock(&fsl_ifc_nand_mutex); + ifc_nand_ctrl->counter--; + if (!ifc_nand_ctrl->counter) { + fsl_ifc_ctrl_dev->nand = NULL; + kfree(ifc_nand_ctrl); + } + mutex_unlock(&fsl_ifc_nand_mutex); + + return 0; +} + +static const struct of_device_id fsl_ifc_nand_match[] = { + { + .compatible = "fsl,ifc-nand", + }, + {} +}; + +static struct platform_driver fsl_ifc_nand_driver = { + .driver = { + .name = "fsl,ifc-nand", + .owner = THIS_MODULE, + .of_match_table = fsl_ifc_nand_match, + }, + .probe = fsl_ifc_nand_probe, + .remove = fsl_ifc_nand_remove, +}; + +static int __init fsl_ifc_nand_init(void) +{ + int ret; + + ret = platform_driver_register(&fsl_ifc_nand_driver); + if (ret) + printk(KERN_ERR "fsl-ifc: Failed to register platform" + "driver\n"); + + return ret; +} + +static void __exit fsl_ifc_nand_exit(void) +{ + platform_driver_unregister(&fsl_ifc_nand_driver); +} + +module_init(fsl_ifc_nand_init); +module_exit(fsl_ifc_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale"); +MODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver"); diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index e53b7606413..1b8330e1155 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -17,6 +17,10 @@ */ #include <linux/clk.h> +#include <linux/completion.h> +#include <linux/dmaengine.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/init.h> #include <linux/module.h> @@ -27,6 +31,7 @@ #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/platform_device.h> +#include <linux/of.h> #include <linux/mtd/partitions.h> #include <linux/io.h> #include <linux/slab.h> @@ -34,7 +39,7 @@ #include <linux/amba/bus.h> #include <mtd/mtd-abi.h> -static struct nand_ecclayout fsmc_ecc1_layout = { +static struct nand_ecclayout fsmc_ecc1_128_layout = { .eccbytes = 24, .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, @@ -50,7 +55,127 @@ static struct nand_ecclayout fsmc_ecc1_layout = { } }; -static struct nand_ecclayout fsmc_ecc4_lp_layout = { +static struct nand_ecclayout fsmc_ecc1_64_layout = { + .eccbytes = 12, + .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52}, + .oobfree = { + {.offset = 8, .length = 8}, + {.offset = 24, .length = 8}, + {.offset = 40, .length = 8}, + {.offset = 56, .length = 8}, + } +}; + +static struct nand_ecclayout fsmc_ecc1_16_layout = { + .eccbytes = 3, + .eccpos = {2, 3, 4}, + .oobfree = { + {.offset = 8, .length = 8}, + } +}; + +/* + * ECC4 layout for NAND of pagesize 8192 bytes & OOBsize 256 bytes. 13*16 bytes + * of OB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 46 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_256_layout = { + .eccbytes = 208, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, + 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, + 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, + 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, + 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, + 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, + 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, + 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, + 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 3}, + {.offset = 143, .length = 3}, + {.offset = 159, .length = 3}, + {.offset = 175, .length = 3}, + {.offset = 191, .length = 3}, + {.offset = 207, .length = 3}, + {.offset = 223, .length = 3}, + {.offset = 239, .length = 3}, + {.offset = 255, .length = 1} + } +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_224_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 97} + } +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 128 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 22 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_128_layout = { .eccbytes = 104, .eccpos = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, @@ -82,6 +207,45 @@ static struct nand_ecclayout fsmc_ecc4_lp_layout = { }; /* + * ECC4 layout for NAND of pagesize 2048 bytes & OOBsize 64 bytes. 13*4 bytes of + * OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 10 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_64_layout = { + .eccbytes = 52, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 1}, + } +}; + +/* + * ECC4 layout for NAND of pagesize 512 bytes & OOBsize 16 bytes. 13 bytes of + * OOB size is reserved for ECC, Byte no. 4 & 5 reserved for bad block and One + * byte is free for use. + */ +static struct nand_ecclayout fsmc_ecc4_16_layout = { + .eccbytes = 13, + .eccpos = { 0, 1, 2, 3, 6, 7, 8, + 9, 10, 11, 12, 13, 14 + }, + .oobfree = { + {.offset = 15, .length = 1}, + } +}; + +/* * ECC placement definitions in oobfree type format. * There are 13 bytes of ecc for every 512 byte block and it has to be read * consecutively and immediately after the 512 byte data block for hardware to @@ -103,16 +267,6 @@ static struct fsmc_eccplace fsmc_ecc4_lp_place = { } }; -static struct nand_ecclayout fsmc_ecc4_sp_layout = { - .eccbytes = 13, - .eccpos = { 0, 1, 2, 3, 6, 7, 8, - 9, 10, 11, 12, 13, 14 - }, - .oobfree = { - {.offset = 15, .length = 1}, - } -}; - static struct fsmc_eccplace fsmc_ecc4_sp_place = { .eccplace = { {.offset = 0, .length = 4}, @@ -120,75 +274,24 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = { } }; -/* - * Default partition tables to be used if the partition information not - * provided through platform data. - * - * Default partition layout for small page(= 512 bytes) devices - * Size for "Root file system" is updated in driver based on actual device size - */ -static struct mtd_partition partition_info_16KB_blk[] = { - { - .name = "X-loader", - .offset = 0, - .size = 4*0x4000, - }, - { - .name = "U-Boot", - .offset = 0x10000, - .size = 20*0x4000, - }, - { - .name = "Kernel", - .offset = 0x60000, - .size = 256*0x4000, - }, - { - .name = "Root File System", - .offset = 0x460000, - .size = MTDPART_SIZ_FULL, - }, -}; - -/* - * Default partition layout for large page(> 512 bytes) devices - * Size for "Root file system" is updated in driver based on actual device size - */ -static struct mtd_partition partition_info_128KB_blk[] = { - { - .name = "X-loader", - .offset = 0, - .size = 4*0x20000, - }, - { - .name = "U-Boot", - .offset = 0x80000, - .size = 12*0x20000, - }, - { - .name = "Kernel", - .offset = 0x200000, - .size = 48*0x20000, - }, - { - .name = "Root File System", - .offset = 0x800000, - .size = MTDPART_SIZ_FULL, - }, -}; - - /** * struct fsmc_nand_data - structure for FSMC NAND device state * * @pid: Part ID on the AMBA PrimeCell format * @mtd: MTD info for a NAND flash. * @nand: Chip related info for a NAND flash. + * @partitions: Partition info for a NAND Flash. + * @nr_partitions: Total number of partition of a NAND flash. * * @ecc_place: ECC placing locations in oobfree type format. * @bank: Bank number for probed device. * @clk: Clock structure for FSMC. * + * @read_dma_chan: DMA channel for read access + * @write_dma_chan: DMA channel for write access to NAND + * @dma_access_complete: Completion structure + * + * @data_pa: NAND Physical port for Data. * @data_va: NAND port for Data. * @cmd_va: NAND port for Command. * @addr_va: NAND port for Address. @@ -198,16 +301,23 @@ struct fsmc_nand_data { u32 pid; struct mtd_info mtd; struct nand_chip nand; + struct mtd_partition *partitions; + unsigned int nr_partitions; struct fsmc_eccplace *ecc_place; unsigned int bank; + struct device *dev; + enum access_mode mode; struct clk *clk; - struct resource *resregs; - struct resource *rescmd; - struct resource *resaddr; - struct resource *resdata; + /* DMA related objects */ + struct dma_chan *read_dma_chan; + struct dma_chan *write_dma_chan; + struct completion dma_access_complete; + + struct fsmc_nand_timings *dev_timings; + dma_addr_t data_pa; void __iomem *data_va; void __iomem *cmd_va; void __iomem *addr_va; @@ -251,28 +361,29 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) struct nand_chip *this = mtd->priv; struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void *__iomem *regs = host->regs_va; unsigned int bank = host->bank; if (ctrl & NAND_CTRL_CHANGE) { + u32 pc; + if (ctrl & NAND_CLE) { - this->IO_ADDR_R = (void __iomem *)host->cmd_va; - this->IO_ADDR_W = (void __iomem *)host->cmd_va; + this->IO_ADDR_R = host->cmd_va; + this->IO_ADDR_W = host->cmd_va; } else if (ctrl & NAND_ALE) { - this->IO_ADDR_R = (void __iomem *)host->addr_va; - this->IO_ADDR_W = (void __iomem *)host->addr_va; + this->IO_ADDR_R = host->addr_va; + this->IO_ADDR_W = host->addr_va; } else { - this->IO_ADDR_R = (void __iomem *)host->data_va; - this->IO_ADDR_W = (void __iomem *)host->data_va; + this->IO_ADDR_R = host->data_va; + this->IO_ADDR_W = host->data_va; } - if (ctrl & NAND_NCE) { - writel(readl(®s->bank_regs[bank].pc) | FSMC_ENABLE, - ®s->bank_regs[bank].pc); - } else { - writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ENABLE, - ®s->bank_regs[bank].pc); - } + pc = readl(FSMC_NAND_REG(regs, bank, PC)); + if (ctrl & NAND_NCE) + pc |= FSMC_ENABLE; + else + pc &= ~FSMC_ENABLE; + writel(pc, FSMC_NAND_REG(regs, bank, PC)); } mb(); @@ -287,22 +398,42 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) * This routine initializes timing parameters related to NAND memory access in * FSMC registers */ -static void __init fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank, - uint32_t busw) +static void fsmc_nand_setup(void __iomem *regs, uint32_t bank, + uint32_t busw, struct fsmc_nand_timings *timings) { uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; + uint32_t tclr, tar, thiz, thold, twait, tset; + struct fsmc_nand_timings *tims; + struct fsmc_nand_timings default_timings = { + .tclr = FSMC_TCLR_1, + .tar = FSMC_TAR_1, + .thiz = FSMC_THIZ_1, + .thold = FSMC_THOLD_4, + .twait = FSMC_TWAIT_6, + .tset = FSMC_TSET_0, + }; + + if (timings) + tims = timings; + else + tims = &default_timings; + + tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; + tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; + thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT; + thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT; + twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT; + tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; if (busw) - writel(value | FSMC_DEVWID_16, ®s->bank_regs[bank].pc); + writel(value | FSMC_DEVWID_16, FSMC_NAND_REG(regs, bank, PC)); else - writel(value | FSMC_DEVWID_8, ®s->bank_regs[bank].pc); - - writel(readl(®s->bank_regs[bank].pc) | FSMC_TCLR_1 | FSMC_TAR_1, - ®s->bank_regs[bank].pc); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - ®s->bank_regs[bank].comm); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - ®s->bank_regs[bank].attrib); + writel(value | FSMC_DEVWID_8, FSMC_NAND_REG(regs, bank, PC)); + + writel(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar, + FSMC_NAND_REG(regs, bank, PC)); + writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, COMM)); + writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, ATTRIB)); } /* @@ -312,15 +443,15 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void __iomem *regs = host->regs_va; uint32_t bank = host->bank; - writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ECCPLEN_256, - ®s->bank_regs[bank].pc); - writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ECCEN, - ®s->bank_regs[bank].pc); - writel(readl(®s->bank_regs[bank].pc) | FSMC_ECCEN, - ®s->bank_regs[bank].pc); + writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256, + FSMC_NAND_REG(regs, bank, PC)); + writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN, + FSMC_NAND_REG(regs, bank, PC)); + writel(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN, + FSMC_NAND_REG(regs, bank, PC)); } /* @@ -333,37 +464,42 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void __iomem *regs = host->regs_va; uint32_t bank = host->bank; uint32_t ecc_tmp; unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; do { - if (readl(®s->bank_regs[bank].sts) & FSMC_CODE_RDY) + if (readl(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) break; else cond_resched(); } while (!time_after_eq(jiffies, deadline)); - ecc_tmp = readl(®s->bank_regs[bank].ecc1); + if (time_after_eq(jiffies, deadline)) { + dev_err(host->dev, "calculate ecc timed out\n"); + return -ETIMEDOUT; + } + + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1)); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); ecc[2] = (uint8_t) (ecc_tmp >> 16); ecc[3] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl(®s->bank_regs[bank].ecc2); + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC2)); ecc[4] = (uint8_t) (ecc_tmp >> 0); ecc[5] = (uint8_t) (ecc_tmp >> 8); ecc[6] = (uint8_t) (ecc_tmp >> 16); ecc[7] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl(®s->bank_regs[bank].ecc3); + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC3)); ecc[8] = (uint8_t) (ecc_tmp >> 0); ecc[9] = (uint8_t) (ecc_tmp >> 8); ecc[10] = (uint8_t) (ecc_tmp >> 16); ecc[11] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl(®s->bank_regs[bank].sts); + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, STS)); ecc[12] = (uint8_t) (ecc_tmp >> 16); return 0; @@ -379,11 +515,11 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void __iomem *regs = host->regs_va; uint32_t bank = host->bank; uint32_t ecc_tmp; - ecc_tmp = readl(®s->bank_regs[bank].ecc1); + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1)); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); ecc[2] = (uint8_t) (ecc_tmp >> 16); @@ -391,6 +527,166 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, return 0; } +/* Count the number of 0's in buff upto a max of max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static void dma_complete(void *param) +{ + struct fsmc_nand_data *host = param; + + complete(&host->dma_access_complete); +} + +static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, + enum dma_data_direction direction) +{ + struct dma_chan *chan; + struct dma_device *dma_dev; + struct dma_async_tx_descriptor *tx; + dma_addr_t dma_dst, dma_src, dma_addr; + dma_cookie_t cookie; + unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + int ret; + + if (direction == DMA_TO_DEVICE) + chan = host->write_dma_chan; + else if (direction == DMA_FROM_DEVICE) + chan = host->read_dma_chan; + else + return -EINVAL; + + dma_dev = chan->device; + dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); + + if (direction == DMA_TO_DEVICE) { + dma_src = dma_addr; + dma_dst = host->data_pa; + flags |= DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_SKIP_DEST_UNMAP; + } else { + dma_src = host->data_pa; + dma_dst = dma_addr; + flags |= DMA_COMPL_DEST_UNMAP_SINGLE | DMA_COMPL_SKIP_SRC_UNMAP; + } + + tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, + len, flags); + + if (!tx) { + dev_err(host->dev, "device_prep_dma_memcpy error\n"); + dma_unmap_single(dma_dev->dev, dma_addr, len, direction); + return -EIO; + } + + tx->callback = dma_complete; + tx->callback_param = host; + cookie = tx->tx_submit(tx); + + ret = dma_submit_error(cookie); + if (ret) { + dev_err(host->dev, "dma_submit_error %d\n", cookie); + return ret; + } + + dma_async_issue_pending(chan); + + ret = + wait_for_completion_interruptible_timeout(&host->dma_access_complete, + msecs_to_jiffies(3000)); + if (ret <= 0) { + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dev_err(host->dev, "wait_for_completion_timeout\n"); + return ret ? ret : -ETIMEDOUT; + } + + return 0; +} + +/* + * fsmc_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && + IS_ALIGNED(len, sizeof(uint32_t))) { + uint32_t *p = (uint32_t *)buf; + len = len >> 2; + for (i = 0; i < len; i++) + writel(p[i], chip->IO_ADDR_W); + } else { + for (i = 0; i < len; i++) + writeb(buf[i], chip->IO_ADDR_W); + } +} + +/* + * fsmc_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && + IS_ALIGNED(len, sizeof(uint32_t))) { + uint32_t *p = (uint32_t *)buf; + len = len >> 2; + for (i = 0; i < len; i++) + p[i] = readl(chip->IO_ADDR_R); + } else { + for (i = 0; i < len; i++) + buf[i] = readb(chip->IO_ADDR_R); + } +} + +/* + * fsmc_read_buf_dma - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct fsmc_nand_data *host; + + host = container_of(mtd, struct fsmc_nand_data, mtd); + dma_xfer(host, buf, len, DMA_FROM_DEVICE); +} + +/* + * fsmc_write_buf_dma - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct fsmc_nand_data *host; + + host = container_of(mtd, struct fsmc_nand_data, mtd); + dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); +} + /* * fsmc_read_page_hwecc * @mtd: mtd info structure @@ -426,7 +722,6 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *oob = (uint8_t *)&ecc_oob[0]; for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { - chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); @@ -437,17 +732,19 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, group++; /* - * length is intentionally kept a higher multiple of 2 - * to read at least 13 bytes even in case of 16 bit NAND - * devices - */ - len = roundup(len, 2); + * length is intentionally kept a higher multiple of 2 + * to read at least 13 bytes even in case of 16 bit NAND + * devices + */ + if (chip->options & NAND_BUSWIDTH_16) + len = roundup(len, 2); + chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); chip->read_buf(mtd, oob + j, len); j += len; } - memcpy(&ecc_code[i], oob, 13); + memcpy(&ecc_code[i], oob, chip->ecc.bytes); chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); @@ -461,7 +758,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, } /* - * fsmc_correct_data + * fsmc_bch8_correct_data * @mtd: mtd info structure * @dat: buffer of read data * @read_ecc: ecc read from device spare area @@ -470,19 +767,51 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * calc_ecc is a 104 bit information containing maximum of 8 error * offset informations of 13 bits each in 512 bytes of read data. */ -static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, +static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + struct nand_chip *chip = mtd->priv; + void __iomem *regs = host->regs_va; unsigned int bank = host->bank; - uint16_t err_idx[8]; - uint64_t ecc_data[2]; + uint32_t err_idx[8]; uint32_t num_err, i; + uint32_t ecc1, ecc2, ecc3, ecc4; + + num_err = (readl(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF; + + /* no bit flipping */ + if (likely(num_err == 0)) + return 0; + + /* too many errors */ + if (unlikely(num_err > 8)) { + /* + * This is a temporary erase check. A newly erased page read + * would result in an ecc error because the oob data is also + * erased to FF and the calculated ecc for an FF data is not + * FF..FF. + * This is a workaround to skip performing correction in case + * data is FF..FF + * + * Logic: + * For every page, each bit written as 0 is counted until these + * number of bits are greater than 8 (the maximum correction + * capability of FSMC for each 512 + 13 bytes) + */ + + int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8); + int bits_data = count_written_bits(dat, chip->ecc.size, 8); + + if ((bits_ecc + bits_data) <= 8) { + if (bits_data) + memset(dat, 0xff, chip->ecc.size); + return bits_data; + } - /* The calculated ecc is actually the correction index in data */ - memcpy(ecc_data, calc_ecc, 13); + return -EBADMSG; + } /* * ------------------- calc_ecc[] bit wise -----------|--13 bits--| @@ -493,27 +822,26 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, * uint64_t array and error offset indexes are populated in err_idx * array */ - for (i = 0; i < 8; i++) { - if (i == 4) { - err_idx[4] = ((ecc_data[1] & 0x1) << 12) | ecc_data[0]; - ecc_data[1] >>= 1; - continue; - } - err_idx[i] = (ecc_data[i/4] & 0x1FFF); - ecc_data[i/4] >>= 13; - } - - num_err = (readl(®s->bank_regs[bank].sts) >> 10) & 0xF; - - if (num_err == 0xF) - return -EBADMSG; + ecc1 = readl(FSMC_NAND_REG(regs, bank, ECC1)); + ecc2 = readl(FSMC_NAND_REG(regs, bank, ECC2)); + ecc3 = readl(FSMC_NAND_REG(regs, bank, ECC3)); + ecc4 = readl(FSMC_NAND_REG(regs, bank, STS)); + + err_idx[0] = (ecc1 >> 0) & 0x1FFF; + err_idx[1] = (ecc1 >> 13) & 0x1FFF; + err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); + err_idx[3] = (ecc2 >> 7) & 0x1FFF; + err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); + err_idx[5] = (ecc3 >> 1) & 0x1FFF; + err_idx[6] = (ecc3 >> 14) & 0x1FFF; + err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); i = 0; while (num_err--) { change_bit(0, (unsigned long *)&err_idx[i]); change_bit(1, (unsigned long *)&err_idx[i]); - if (err_idx[i] <= 512 * 8) { + if (err_idx[i] < chip->ecc.size * 8) { change_bit(err_idx[i], (unsigned long *)dat); i++; } @@ -521,6 +849,44 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, return i; } +static bool filter(struct dma_chan *chan, void *slave) +{ + chan->private = slave; + return true; +} + +#ifdef CONFIG_OF +static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev, + struct device_node *np) +{ + struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + u32 val; + + /* Set default NAND width to 8 bits */ + pdata->width = 8; + if (!of_property_read_u32(np, "bank-width", &val)) { + if (val == 2) { + pdata->width = 16; + } else if (val != 1) { + dev_err(&pdev->dev, "invalid bank-width %u\n", val); + return -EINVAL; + } + } + of_property_read_u32(np, "st,ale-off", &pdata->ale_off); + of_property_read_u32(np, "st,cle-off", &pdata->cle_off); + if (of_get_property(np, "nand-skip-bbtscan", NULL)) + pdata->options = NAND_SKIP_BBTSCAN; + + return 0; +} +#else +static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev, + struct device_node *np) +{ + return -ENOSYS; +} +#endif + /* * fsmc_nand_probe - Probe function * @pdev: platform device structure @@ -528,102 +894,109 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, static int __init fsmc_nand_probe(struct platform_device *pdev) { struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device_node __maybe_unused *np = pdev->dev.of_node; + struct mtd_part_parser_data ppdata = {}; struct fsmc_nand_data *host; struct mtd_info *mtd; struct nand_chip *nand; - struct fsmc_regs *regs; struct resource *res; + dma_cap_mask_t mask; int ret = 0; u32 pid; int i; + if (np) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdev->dev.platform_data = pdata; + ret = fsmc_nand_probe_config_dt(pdev, np); + if (ret) { + dev_err(&pdev->dev, "no platform data\n"); + return -ENODEV; + } + } + if (!pdata) { dev_err(&pdev->dev, "platform data is NULL\n"); return -EINVAL; } /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(*host), GFP_KERNEL); + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) { dev_err(&pdev->dev, "failed to allocate device structure\n"); return -ENOMEM; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - if (!res) { - ret = -EIO; - goto err_probe1; - } + if (!res) + return -EINVAL; - host->resdata = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!host->resdata) { - ret = -EIO; - goto err_probe1; + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, "Failed to get memory data resourse\n"); + return -ENOENT; } - host->data_va = ioremap(res->start, resource_size(res)); + host->data_pa = (dma_addr_t)res->start; + host->data_va = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (!host->data_va) { - ret = -EIO; - goto err_probe1; + dev_err(&pdev->dev, "data ioremap failed\n"); + return -ENOMEM; } - host->resaddr = request_mem_region(res->start + PLAT_NAND_ALE, - resource_size(res), pdev->name); - if (!host->resaddr) { - ret = -EIO; - goto err_probe1; + if (!devm_request_mem_region(&pdev->dev, res->start + pdata->ale_off, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Failed to get memory ale resourse\n"); + return -ENOENT; } - host->addr_va = ioremap(res->start + PLAT_NAND_ALE, resource_size(res)); + host->addr_va = devm_ioremap(&pdev->dev, res->start + pdata->ale_off, + resource_size(res)); if (!host->addr_va) { - ret = -EIO; - goto err_probe1; + dev_err(&pdev->dev, "ale ioremap failed\n"); + return -ENOMEM; } - host->rescmd = request_mem_region(res->start + PLAT_NAND_CLE, - resource_size(res), pdev->name); - if (!host->rescmd) { - ret = -EIO; - goto err_probe1; + if (!devm_request_mem_region(&pdev->dev, res->start + pdata->cle_off, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Failed to get memory cle resourse\n"); + return -ENOENT; } - host->cmd_va = ioremap(res->start + PLAT_NAND_CLE, resource_size(res)); + host->cmd_va = devm_ioremap(&pdev->dev, res->start + pdata->cle_off, + resource_size(res)); if (!host->cmd_va) { - ret = -EIO; - goto err_probe1; + dev_err(&pdev->dev, "ale ioremap failed\n"); + return -ENOMEM; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); - if (!res) { - ret = -EIO; - goto err_probe1; - } + if (!res) + return -EINVAL; - host->resregs = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!host->resregs) { - ret = -EIO; - goto err_probe1; + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, "Failed to get memory regs resourse\n"); + return -ENOENT; } - host->regs_va = ioremap(res->start, resource_size(res)); + host->regs_va = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (!host->regs_va) { - ret = -EIO; - goto err_probe1; + dev_err(&pdev->dev, "regs ioremap failed\n"); + return -ENOMEM; } host->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "failed to fetch block clock\n"); - ret = PTR_ERR(host->clk); - host->clk = NULL; - goto err_probe1; + return PTR_ERR(host->clk); } ret = clk_enable(host->clk); if (ret) - goto err_probe1; + goto err_clk_enable; /* * This device ID is actually a common AMBA ID as used on the @@ -639,7 +1012,14 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->bank = pdata->bank; host->select_chip = pdata->select_bank; - regs = host->regs_va; + host->partitions = pdata->partitions; + host->nr_partitions = pdata->nr_partitions; + host->dev = &pdev->dev; + host->dev_timings = pdata->nand_timings; + host->mode = pdata->mode; + + if (host->mode == USE_DMA_ACCESS) + init_completion(&host->dma_access_complete); /* Link all private pointers */ mtd = &host->mtd; @@ -658,21 +1038,53 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.size = 512; nand->options = pdata->options; nand->select_chip = fsmc_select_chip; + nand->badblockbits = 7; if (pdata->width == FSMC_NAND_BW16) nand->options |= NAND_BUSWIDTH_16; - fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16); + switch (host->mode) { + case USE_DMA_ACCESS: + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + host->read_dma_chan = dma_request_channel(mask, filter, + pdata->read_dma_priv); + if (!host->read_dma_chan) { + dev_err(&pdev->dev, "Unable to get read dma channel\n"); + goto err_req_read_chnl; + } + host->write_dma_chan = dma_request_channel(mask, filter, + pdata->write_dma_priv); + if (!host->write_dma_chan) { + dev_err(&pdev->dev, "Unable to get write dma channel\n"); + goto err_req_write_chnl; + } + nand->read_buf = fsmc_read_buf_dma; + nand->write_buf = fsmc_write_buf_dma; + break; + + default: + case USE_WORD_ACCESS: + nand->read_buf = fsmc_read_buf; + nand->write_buf = fsmc_write_buf; + break; + } + + fsmc_nand_setup(host->regs_va, host->bank, + nand->options & NAND_BUSWIDTH_16, + host->dev_timings); if (AMBA_REV_BITS(host->pid) >= 8) { nand->ecc.read_page = fsmc_read_page_hwecc; nand->ecc.calculate = fsmc_read_hwecc_ecc4; - nand->ecc.correct = fsmc_correct_data; + nand->ecc.correct = fsmc_bch8_correct_data; nand->ecc.bytes = 13; + nand->ecc.strength = 8; } else { nand->ecc.calculate = fsmc_read_hwecc_ecc1; nand->ecc.correct = nand_correct_data; nand->ecc.bytes = 3; + nand->ecc.strength = 1; } /* @@ -681,19 +1093,52 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (nand_scan_ident(&host->mtd, 1, NULL)) { ret = -ENXIO; dev_err(&pdev->dev, "No NAND Device found!\n"); - goto err_probe; + goto err_scan_ident; } if (AMBA_REV_BITS(host->pid) >= 8) { - if (host->mtd.writesize == 512) { - nand->ecc.layout = &fsmc_ecc4_sp_layout; + switch (host->mtd.oobsize) { + case 16: + nand->ecc.layout = &fsmc_ecc4_16_layout; host->ecc_place = &fsmc_ecc4_sp_place; - } else { - nand->ecc.layout = &fsmc_ecc4_lp_layout; + break; + case 64: + nand->ecc.layout = &fsmc_ecc4_64_layout; + host->ecc_place = &fsmc_ecc4_lp_place; + break; + case 128: + nand->ecc.layout = &fsmc_ecc4_128_layout; + host->ecc_place = &fsmc_ecc4_lp_place; + break; + case 224: + nand->ecc.layout = &fsmc_ecc4_224_layout; host->ecc_place = &fsmc_ecc4_lp_place; + break; + case 256: + nand->ecc.layout = &fsmc_ecc4_256_layout; + host->ecc_place = &fsmc_ecc4_lp_place; + break; + default: + printk(KERN_WARNING "No oob scheme defined for " + "oobsize %d\n", mtd->oobsize); + BUG(); } } else { - nand->ecc.layout = &fsmc_ecc1_layout; + switch (host->mtd.oobsize) { + case 16: + nand->ecc.layout = &fsmc_ecc1_16_layout; + break; + case 64: + nand->ecc.layout = &fsmc_ecc1_64_layout; + break; + case 128: + nand->ecc.layout = &fsmc_ecc1_128_layout; + break; + default: + printk(KERN_WARNING "No oob scheme defined for " + "oobsize %d\n", mtd->oobsize); + BUG(); + } } /* Second stage of scan to fill MTD data-structures */ @@ -713,13 +1158,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * Check for partition info passed */ host->mtd.name = "nand"; - ret = mtd_device_parse_register(&host->mtd, NULL, 0, - host->mtd.size <= 0x04000000 ? - partition_info_16KB_blk : - partition_info_128KB_blk, - host->mtd.size <= 0x04000000 ? - ARRAY_SIZE(partition_info_16KB_blk) : - ARRAY_SIZE(partition_info_128KB_blk)); + ppdata.of_node = np; + ret = mtd_device_parse_register(&host->mtd, NULL, &ppdata, + host->partitions, host->nr_partitions); if (ret) goto err_probe; @@ -728,32 +1169,16 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) return 0; err_probe: +err_scan_ident: + if (host->mode == USE_DMA_ACCESS) + dma_release_channel(host->write_dma_chan); +err_req_write_chnl: + if (host->mode == USE_DMA_ACCESS) + dma_release_channel(host->read_dma_chan); +err_req_read_chnl: clk_disable(host->clk); -err_probe1: - if (host->clk) - clk_put(host->clk); - if (host->regs_va) - iounmap(host->regs_va); - if (host->resregs) - release_mem_region(host->resregs->start, - resource_size(host->resregs)); - if (host->cmd_va) - iounmap(host->cmd_va); - if (host->rescmd) - release_mem_region(host->rescmd->start, - resource_size(host->rescmd)); - if (host->addr_va) - iounmap(host->addr_va); - if (host->resaddr) - release_mem_region(host->resaddr->start, - resource_size(host->resaddr)); - if (host->data_va) - iounmap(host->data_va); - if (host->resdata) - release_mem_region(host->resdata->start, - resource_size(host->resdata)); - - kfree(host); +err_clk_enable: + clk_put(host->clk); return ret; } @@ -768,24 +1193,15 @@ static int fsmc_nand_remove(struct platform_device *pdev) if (host) { nand_release(&host->mtd); + + if (host->mode == USE_DMA_ACCESS) { + dma_release_channel(host->write_dma_chan); + dma_release_channel(host->read_dma_chan); + } clk_disable(host->clk); clk_put(host->clk); - - iounmap(host->regs_va); - release_mem_region(host->resregs->start, - resource_size(host->resregs)); - iounmap(host->cmd_va); - release_mem_region(host->rescmd->start, - resource_size(host->rescmd)); - iounmap(host->addr_va); - release_mem_region(host->resaddr->start, - resource_size(host->resaddr)); - iounmap(host->data_va); - release_mem_region(host->resdata->start, - resource_size(host->resdata)); - - kfree(host); } + return 0; } @@ -801,15 +1217,24 @@ static int fsmc_nand_suspend(struct device *dev) static int fsmc_nand_resume(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); - if (host) + if (host) { clk_enable(host->clk); + fsmc_nand_setup(host->regs_va, host->bank, + host->nand.options & NAND_BUSWIDTH_16, + host->dev_timings); + } return 0; } -static const struct dev_pm_ops fsmc_nand_pm_ops = { - .suspend = fsmc_nand_suspend, - .resume = fsmc_nand_resume, +static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id fsmc_nand_id_table[] = { + { .compatible = "st,spear600-fsmc-nand" }, + {} }; +MODULE_DEVICE_TABLE(of, fsmc_nand_id_table); #endif static struct platform_driver fsmc_nand_driver = { @@ -817,6 +1242,7 @@ static struct platform_driver fsmc_nand_driver = { .driver = { .owner = THIS_MODULE, .name = "fsmc-nand", + .of_match_table = of_match_ptr(fsmc_nand_id_table), #ifdef CONFIG_PM .pm = &fsmc_nand_pm_ops, #endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 7db6555ed3b..e8ea7107932 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -835,7 +835,7 @@ int gpmi_send_command(struct gpmi_nand_data *this) | BM_GPMI_CTRL0_ADDRESS_INCREMENT | BF_GPMI_CTRL0_XFER_COUNT(this->command_length); pio[1] = pio[2] = 0; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { @@ -848,8 +848,10 @@ int gpmi_send_command(struct gpmi_nand_data *this) sg_init_one(sgl, this->cmd_buffer, this->command_length); dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); - desc = channel->device->device_prep_slave_sg(channel, - sgl, 1, DMA_MEM_TO_DEV, 1); + desc = dmaengine_prep_slave_sg(channel, + sgl, 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { pr_err("step 2 error\n"); return -1; @@ -880,8 +882,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) | BF_GPMI_CTRL0_ADDRESS(address) | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); pio[1] = 0; - desc = channel->device->device_prep_slave_sg(channel, - (struct scatterlist *)pio, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { pr_err("step 1 error\n"); @@ -890,8 +891,9 @@ int gpmi_send_data(struct gpmi_nand_data *this) /* [2] send DMA request */ prepare_data_dma(this, DMA_TO_DEVICE); - desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_MEM_TO_DEV, 1); + desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, + 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -916,7 +918,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); pio[1] = 0; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { @@ -926,8 +928,9 @@ int gpmi_read_data(struct gpmi_nand_data *this) /* [2] : send DMA request */ prepare_data_dma(this, DMA_FROM_DEVICE); - desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_DEV_TO_MEM, 1); + desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, + 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -972,9 +975,10 @@ int gpmi_send_page(struct gpmi_nand_data *this, pio[4] = payload; pio[5] = auxiliary; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); + ARRAY_SIZE(pio), DMA_TRANS_NONE, + DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -1007,7 +1011,7 @@ int gpmi_read_page(struct gpmi_nand_data *this, | BF_GPMI_CTRL0_ADDRESS(address) | BF_GPMI_CTRL0_XFER_COUNT(0); pio[1] = 0; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, 2, DMA_TRANS_NONE, 0); if (!desc) { @@ -1036,9 +1040,10 @@ int gpmi_read_page(struct gpmi_nand_data *this, pio[3] = geo->page_size; pio[4] = payload; pio[5] = auxiliary; - desc = channel->device->device_prep_slave_sg(channel, + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 1); + ARRAY_SIZE(pio), DMA_TRANS_NONE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -1055,9 +1060,11 @@ int gpmi_read_page(struct gpmi_nand_data *this, | BF_GPMI_CTRL0_ADDRESS(address) | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); pio[1] = 0; - desc = channel->device->device_prep_slave_sg(channel, - (struct scatterlist *)pio, 2, - DMA_TRANS_NONE, 1); + pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */ + desc = dmaengine_prep_slave_sg(channel, + (struct scatterlist *)pio, 3, + DMA_TRANS_NONE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 3 error\n"); return -1; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 493ec2fcf97..75b1dde1635 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1124,7 +1124,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); /* Do we have a flash based bad block table ? */ - if (chip->options & NAND_BBT_USE_FLASH) + if (chip->bbt_options & NAND_BBT_USE_FLASH) ret = nand_update_bbt(mtd, ofs); else { chipnr = (int)(ofs >> chip->chip_shift); @@ -1155,7 +1155,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) return ret; } -static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this) +static int nand_boot_set_geometry(struct gpmi_nand_data *this) { struct boot_rom_geometry *geometry = &this->rom_geometry; @@ -1182,7 +1182,7 @@ static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this) } static const char *fingerprint = "STMP"; -static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this) +static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) { struct boot_rom_geometry *rom_geo = &this->rom_geometry; struct device *dev = this->dev; @@ -1239,7 +1239,7 @@ static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this) } /* Writes a transcription stamp. */ -static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this) +static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) { struct device *dev = this->dev; struct boot_rom_geometry *rom_geo = &this->rom_geometry; @@ -1322,7 +1322,7 @@ static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this) return 0; } -static int __devinit mx23_boot_init(struct gpmi_nand_data *this) +static int mx23_boot_init(struct gpmi_nand_data *this) { struct device *dev = this->dev; struct nand_chip *chip = &this->nand; @@ -1391,7 +1391,7 @@ static int __devinit mx23_boot_init(struct gpmi_nand_data *this) return 0; } -static int __devinit nand_boot_init(struct gpmi_nand_data *this) +static int nand_boot_init(struct gpmi_nand_data *this) { nand_boot_set_geometry(this); @@ -1401,7 +1401,7 @@ static int __devinit nand_boot_init(struct gpmi_nand_data *this) return 0; } -static int __devinit gpmi_set_geometry(struct gpmi_nand_data *this) +static int gpmi_set_geometry(struct gpmi_nand_data *this) { int ret; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index e023bccb778..ec6180d4ff8 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -20,7 +20,7 @@ #include <linux/mtd/nand.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> -#include <mach/dma.h> +#include <linux/fsl/mxs-dma.h> struct resources { void *gpmi_regs; diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 5dc6f0d92f1..11e48781342 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -135,8 +135,8 @@ static int __init h1910_init(void) } /* Register the partitions */ - mtd_device_parse_register(h1910_nand_mtd, NULL, 0, - partition_info, NUM_PARTITIONS); + mtd_device_parse_register(h1910_nand_mtd, NULL, NULL, partition_info, + NUM_PARTITIONS); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index ac3b9f255e0..e4147e8acb7 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -332,6 +332,11 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; chip->ecc.size = 512; chip->ecc.bytes = 9; + chip->ecc.strength = 2; + /* + * FIXME: ecc_strength value of 2 bits per 512 bytes of data is a + * conservative guess, given 9 ecc bytes and reed-solomon alg. + */ if (pdata) chip->ecc.layout = pdata->ecc_layout; @@ -367,9 +372,9 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) goto err_gpio_free; } - ret = mtd_device_parse_register(mtd, NULL, 0, - pdata ? pdata->partitions : NULL, - pdata ? pdata->num_partitions : 0); + ret = mtd_device_parse_register(mtd, NULL, NULL, + pdata ? pdata->partitions : NULL, + pdata ? pdata->num_partitions : 0); if (ret) { dev_err(&pdev->dev, "Failed to add mtd device\n"); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 74a43b818d0..cc0678a967c 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1225,9 +1225,16 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } + if (this->ecc.mode == NAND_ECC_HW) { + if (nfc_is_v1()) + this->ecc.strength = 1; + else + this->ecc.strength = (host->eccsize == 4) ? 4 : 8; + } + /* Register the partitions */ - mtd_device_parse_register(mtd, part_probes, 0, - pdata->parts, pdata->nr_parts); + mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts, + pdata->nr_parts); platform_set_drvdata(pdev, host); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8a393f9e602..47b19c0bb07 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -123,12 +123,6 @@ static int check_offs_len(struct mtd_info *mtd, ret = -EINVAL; } - /* Do not allow past end of device */ - if (ofs + len > mtd->size) { - pr_debug("%s: past end of device\n", __func__); - ret = -EINVAL; - } - return ret; } @@ -338,7 +332,7 @@ static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) */ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) { - int page, chipnr, res = 0; + int page, chipnr, res = 0, i = 0; struct nand_chip *chip = mtd->priv; u16 bad; @@ -356,23 +350,29 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) chip->select_chip(mtd, chipnr); } - if (chip->options & NAND_BUSWIDTH_16) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, - page); - bad = cpu_to_le16(chip->read_word(mtd)); - if (chip->badblockpos & 0x1) - bad >>= 8; - else - bad &= 0xFF; - } else { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); - bad = chip->read_byte(mtd); - } + do { + if (chip->options & NAND_BUSWIDTH_16) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, + chip->badblockpos & 0xFE, page); + bad = cpu_to_le16(chip->read_word(mtd)); + if (chip->badblockpos & 0x1) + bad >>= 8; + else + bad &= 0xFF; + } else { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, + page); + bad = chip->read_byte(mtd); + } - if (likely(chip->badblockbits == 8)) - res = bad != 0xFF; - else - res = hweight8(bad) < chip->badblockbits; + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + ofs += mtd->writesize; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + i++; + } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); if (getchip) nand_release_device(mtd); @@ -386,51 +386,79 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) * @ofs: offset from device start * * This is the default implementation, which can be overridden by a hardware - * specific driver. + * specific driver. We try operations in the following order, according to our + * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + * (1) erase the affected block, to allow OOB marker to be written cleanly + * (2) update in-memory BBT + * (3) write bad block marker to OOB area of affected block + * (4) update flash-based BBT + * Note that we retain the first error encountered in (3) or (4), finish the + * procedures, and dump the error in the end. */ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; uint8_t buf[2] = { 0, 0 }; - int block, ret, i = 0; + int block, res, ret = 0, i = 0; + int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - ofs += mtd->erasesize - mtd->writesize; + if (write_oob) { + struct erase_info einfo; + + /* Attempt erase before marking OOB */ + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = ofs; + einfo.len = 1 << chip->phys_erase_shift; + nand_erase_nand(mtd, &einfo, 0); + } /* Get block number */ block = (int)(ofs >> chip->bbt_erase_shift); + /* Mark block bad in memory-based BBT */ if (chip->bbt) chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); - /* Do we have a flash based bad block table? */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) - ret = nand_update_bbt(mtd, ofs); - else { + /* Write bad block marker to OOB */ + if (write_oob) { struct mtd_oob_ops ops; + loff_t wr_ofs = ofs; nand_get_device(chip, mtd, FL_WRITING); - /* - * Write to first two pages if necessary. If we write to more - * than one location, the first error encountered quits the - * procedure. We write two bytes per location, so we dont have - * to mess with 16 bit access. - */ - ops.len = ops.ooblen = 2; ops.datbuf = NULL; ops.oobbuf = buf; - ops.ooboffs = chip->badblockpos & ~0x01; + ops.ooboffs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + ops.ooboffs &= ~0x01; + ops.len = ops.ooblen = 2; + } else { + ops.len = ops.ooblen = 1; + } ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + wr_ofs += mtd->erasesize - mtd->writesize; do { - ret = nand_do_write_oob(mtd, ofs, &ops); + res = nand_do_write_oob(mtd, wr_ofs, &ops); + if (!ret) + ret = res; i++; - ofs += mtd->writesize; - } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && - i < 2); + wr_ofs += mtd->writesize; + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); nand_release_device(mtd); } + + /* Update flash-based bad block table */ + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + res = nand_update_bbt(mtd, ofs); + if (!ret) + ret = res; + } + if (!ret) mtd->ecc_stats.badblocks++; @@ -1586,25 +1614,14 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, struct mtd_oob_ops ops; int ret; - /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) - return -EINVAL; - if (!len) - return 0; - nand_get_device(chip, mtd, FL_READING); - ops.len = len; ops.datbuf = buf; ops.oobbuf = NULL; ops.mode = 0; - ret = nand_do_read_ops(mtd, from, &ops); - *retlen = ops.retlen; - nand_release_device(mtd); - return ret; } @@ -2293,12 +2310,6 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, struct mtd_oob_ops ops; 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); @@ -2333,25 +2344,14 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, struct mtd_oob_ops ops; int ret; - /* Do not allow reads past end of device */ - if ((to + len) > mtd->size) - return -EINVAL; - if (!len) - return 0; - nand_get_device(chip, mtd, FL_WRITING); - ops.len = len; ops.datbuf = (uint8_t *)buf; ops.oobbuf = NULL; ops.mode = 0; - ret = nand_do_write_ops(mtd, to, &ops); - *retlen = ops.retlen; - nand_release_device(mtd); - return ret; } @@ -2550,8 +2550,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; - instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_ERASING); @@ -2715,10 +2713,6 @@ static void nand_sync(struct mtd_info *mtd) */ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) { - /* Check for invalid offset */ - if (offs > mtd->size) - return -EINVAL; - return nand_block_checkbad(mtd, offs, 1, 0); } @@ -2857,7 +2851,6 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') return 0; - pr_info("ONFI flash detected\n"); chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); for (i = 0; i < 3; i++) { chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); @@ -2898,7 +2891,8 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, mtd->writesize = le32_to_cpu(p->byte_per_page); mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize; + chip->chipsize = le32_to_cpu(p->blocks_per_lun); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; *busw = 0; if (le16_to_cpu(p->features) & 1) *busw = NAND_BUSWIDTH_16; @@ -2907,6 +2901,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, chip->options |= (NAND_NO_READRDY | NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK; + pr_info("ONFI flash detected\n"); return 1; } @@ -3238,6 +3233,10 @@ int nand_scan_tail(struct mtd_info *mtd) int i; struct nand_chip *chip = mtd->priv; + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ + BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH)); + if (!(chip->options & NAND_OWN_BUFFERS)) chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); if (!chip->buffers) @@ -3350,6 +3349,7 @@ int nand_scan_tail(struct mtd_info *mtd) if (!chip->ecc.size) chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; break; case NAND_ECC_SOFT_BCH: @@ -3384,6 +3384,8 @@ int nand_scan_tail(struct mtd_info *mtd) pr_warn("BCH ECC initialization failed!\n"); BUG(); } + chip->ecc.strength = + chip->ecc.bytes*8 / fls(8*chip->ecc.size); break; case NAND_ECC_NONE: @@ -3397,6 +3399,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; + chip->ecc.strength = 0; break; default: @@ -3461,25 +3464,26 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : MTD_CAP_NANDFLASH; - mtd->erase = nand_erase; - mtd->point = NULL; - 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; - mtd->lock = NULL; - mtd->unlock = NULL; - mtd->suspend = nand_suspend; - mtd->resume = nand_resume; - mtd->block_isbad = nand_block_isbad; - mtd->block_markbad = nand_block_markbad; + mtd->_erase = nand_erase; + mtd->_point = NULL; + 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; + mtd->_lock = NULL; + mtd->_unlock = NULL; + mtd->_suspend = nand_suspend; + mtd->_resume = nand_resume; + mtd->_block_isbad = nand_block_isbad; + mtd->_block_markbad = nand_block_markbad; mtd->writebufsize = mtd->writesize; - /* propagate ecc.layout to mtd_info */ + /* propagate ecc info to mtd_info */ mtd->ecclayout = chip->ecc.layout; + mtd->ecc_strength = chip->ecc.strength * chip->ecc.steps; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index ec688548c88..2b6f632cf27 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -179,6 +179,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; chip->priv = ndfc; ndfc->mtd.priv = chip; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index b3a883e2a22..c2b0bba9d8b 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1058,6 +1058,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) { info->nand.ecc.bytes = 3; info->nand.ecc.size = 512; + info->nand.ecc.strength = 1; info->nand.ecc.calculate = omap_calculate_ecc; info->nand.ecc.hwctl = omap_enable_hwecc; info->nand.ecc.correct = omap_correct_data; @@ -1101,8 +1102,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) goto out_release_mem_region; } - mtd_device_parse_register(&info->mtd, NULL, 0, - pdata->parts, pdata->nr_parts); + mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts, + pdata->nr_parts); platform_set_drvdata(pdev, &info->mtd); diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 29f505adaf8..1d3bfb26080 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -129,8 +129,8 @@ static int __init orion_nand_probe(struct platform_device *pdev) } mtd->name = "orion_nand"; - ret = mtd_device_parse_register(mtd, NULL, 0, - board->parts, board->nr_parts); + ret = mtd_device_parse_register(mtd, NULL, NULL, board->parts, + board->nr_parts); if (ret) { nand_release(mtd); goto no_dev; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 7f2da695335..6404e6e81b1 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -99,8 +99,9 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) } err = mtd_device_parse_register(&data->mtd, - pdata->chip.part_probe_types, 0, - pdata->chip.partitions, pdata->chip.nr_partitions); + pdata->chip.part_probe_types, NULL, + pdata->chip.partitions, + pdata->chip.nr_partitions); if (!err) return err; diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index 7e52af51a19..0ddd90e5788 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -275,11 +275,10 @@ static int __init ppchameleonevb_init(void) ppchameleon_mtd->name = "ppchameleon-nand"; /* Register the partitions */ - mtd_device_parse_register(ppchameleon_mtd, NULL, 0, - ppchameleon_mtd->size == NAND_SMALL_SIZE ? - partition_info_me : - partition_info_hi, - NUM_PARTITIONS); + mtd_device_parse_register(ppchameleon_mtd, NULL, NULL, + ppchameleon_mtd->size == NAND_SMALL_SIZE ? + partition_info_me : partition_info_hi, + NUM_PARTITIONS); nand_evb_init: /**************************** @@ -365,11 +364,10 @@ static int __init ppchameleonevb_init(void) ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME; /* Register the partitions */ - mtd_device_parse_register(ppchameleonevb_mtd, NULL, 0, - ppchameleon_mtd->size == NAND_SMALL_SIZE ? - partition_info_me : - partition_info_hi, - NUM_PARTITIONS); + mtd_device_parse_register(ppchameleonevb_mtd, NULL, NULL, + ppchameleon_mtd->size == NAND_SMALL_SIZE ? + partition_info_me : partition_info_hi, + NUM_PARTITIONS); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 5c3d719c37e..def50caa6f8 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1002,6 +1002,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) KEEP_CONFIG: chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = host->page_size; + chip->ecc.strength = 1; chip->options = NAND_NO_AUTOINCR; chip->options |= NAND_NO_READRDY; @@ -1228,8 +1229,9 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) continue; } - ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0, - pdata->parts[cs], pdata->nr_parts[cs]); + ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, + NULL, pdata->parts[cs], + pdata->nr_parts[cs]); if (!ret) probe_success = 1; } diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 769a4e096b3..c2040187c81 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -891,6 +891,7 @@ int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->ecc.size = R852_DMA_LEN; chip->ecc.bytes = SM_OOB_SIZE; + chip->ecc.strength = 2; chip->ecc.hwctl = r852_ecc_hwctl; chip->ecc.calculate = r852_ecc_calculate; chip->ecc.correct = r852_ecc_correct; diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index f309addc2fa..e55b5cfbe14 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -527,6 +527,7 @@ static int __init rtc_from4_init(void) this->ecc.mode = NAND_ECC_HW_SYNDROME; this->ecc.size = 512; this->ecc.bytes = 8; + this->ecc.strength = 3; /* return the status of extra status and ECC checks */ this->errstat = rtc_from4_errstat; /* set the nand_oobinfo to support FPGA H/W error detection */ diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 868685db671..91121f33f74 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -751,8 +751,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, if (set) mtd->mtd.name = set->name; - return mtd_device_parse_register(&mtd->mtd, NULL, 0, - set->partitions, set->nr_partitions); + return mtd_device_parse_register(&mtd->mtd, NULL, NULL, + set->partitions, set->nr_partitions); } /** @@ -823,6 +823,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->ecc.calculate = s3c2410_nand_calculate_ecc; chip->ecc.correct = s3c2410_nand_correct_data; chip->ecc.mode = NAND_ECC_HW; + chip->ecc.strength = 1; switch (info->cpu_type) { case TYPE_S3C2410: diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 93b1f74321c..e9b2b260de3 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> @@ -283,7 +284,7 @@ static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t flcmncr_val = readl(FLCMNCR(flctl)) & ~SEL_16BIT; + uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT; uint32_t flcmdcr_val, addr_len_bytes = 0; /* Set SNAND bit if page size is 2048byte */ @@ -303,6 +304,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va break; case NAND_CMD_READ0: case NAND_CMD_READOOB: + case NAND_CMD_RNDOUT: addr_len_bytes = flctl->rw_ADRCNT; flcmdcr_val |= CDSRC_E; if (flctl->chip.options & NAND_BUSWIDTH_16) @@ -320,6 +322,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va break; case NAND_CMD_READID: flcmncr_val &= ~SNAND_E; + flcmdcr_val |= CDSRC_E; addr_len_bytes = ADRCNT_1; break; case NAND_CMD_STATUS: @@ -513,6 +516,8 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, struct sh_flctl *flctl = mtd_to_flctl(mtd); uint32_t read_cmd = 0; + pm_runtime_get_sync(&flctl->pdev->dev); + flctl->read_bytes = 0; if (command != NAND_CMD_PAGEPROG) flctl->index = 0; @@ -525,7 +530,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, execmd_read_page_sector(mtd, page_addr); break; } - empty_fifo(flctl); if (flctl->page_size) set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) | command); @@ -547,7 +551,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, break; } - empty_fifo(flctl); if (flctl->page_size) { set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); @@ -559,15 +562,35 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, flctl->read_bytes = mtd->oobsize; goto read_normal_exit; + case NAND_CMD_RNDOUT: + if (flctl->hwecc) + break; + + if (flctl->page_size) + set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8) + | command); + else + set_cmd_regs(mtd, command, command); + + set_addr(mtd, column, 0); + + flctl->read_bytes = mtd->writesize + mtd->oobsize - column; + goto read_normal_exit; + case NAND_CMD_READID: - empty_fifo(flctl); set_cmd_regs(mtd, command, command); - set_addr(mtd, 0, 0); - flctl->read_bytes = 4; + /* READID is always performed using an 8-bit bus */ + if (flctl->chip.options & NAND_BUSWIDTH_16) + column <<= 1; + set_addr(mtd, column, 0); + + flctl->read_bytes = 8; writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + empty_fifo(flctl); start_translation(flctl); - read_datareg(flctl, 0); /* read and end */ + read_fiforeg(flctl, flctl->read_bytes, 0); + wait_completion(flctl); break; case NAND_CMD_ERASE1: @@ -650,29 +673,55 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, default: break; } - return; + goto runtime_exit; read_normal_exit: writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + empty_fifo(flctl); start_translation(flctl); read_fiforeg(flctl, flctl->read_bytes, 0); wait_completion(flctl); +runtime_exit: + pm_runtime_put_sync(&flctl->pdev->dev); return; } static void flctl_select_chip(struct mtd_info *mtd, int chipnr) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t flcmncr_val = readl(FLCMNCR(flctl)); + int ret; switch (chipnr) { case -1: - flcmncr_val &= ~CE0_ENABLE; - writel(flcmncr_val, FLCMNCR(flctl)); + flctl->flcmncr_base &= ~CE0_ENABLE; + + pm_runtime_get_sync(&flctl->pdev->dev); + writel(flctl->flcmncr_base, FLCMNCR(flctl)); + + if (flctl->qos_request) { + dev_pm_qos_remove_request(&flctl->pm_qos); + flctl->qos_request = 0; + } + + pm_runtime_put_sync(&flctl->pdev->dev); break; case 0: - flcmncr_val |= CE0_ENABLE; - writel(flcmncr_val, FLCMNCR(flctl)); + flctl->flcmncr_base |= CE0_ENABLE; + + if (!flctl->qos_request) { + ret = dev_pm_qos_add_request(&flctl->pdev->dev, + &flctl->pm_qos, 100); + if (ret < 0) + dev_err(&flctl->pdev->dev, + "PM QoS request failed: %d\n", ret); + flctl->qos_request = 1; + } + + if (flctl->holden) { + pm_runtime_get_sync(&flctl->pdev->dev); + writel(HOLDEN, FLHOLDCR(flctl)); + pm_runtime_put_sync(&flctl->pdev->dev); + } break; default: BUG(); @@ -730,11 +779,6 @@ static int flctl_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) return 0; } -static void flctl_register_init(struct sh_flctl *flctl, unsigned long val) -{ - writel(val, FLCMNCR(flctl)); -} - static int flctl_chip_init_tail(struct mtd_info *mtd) { struct sh_flctl *flctl = mtd_to_flctl(mtd); @@ -781,13 +825,13 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) chip->ecc.size = 512; chip->ecc.bytes = 10; + chip->ecc.strength = 4; chip->ecc.read_page = flctl_read_page_hwecc; chip->ecc.write_page = flctl_write_page_hwecc; chip->ecc.mode = NAND_ECC_HW; /* 4 symbols ECC enabled */ - writel(readl(FLCMNCR(flctl)) | _4ECCEN | ECCPOS2 | ECCPOS_02, - FLCMNCR(flctl)); + flctl->flcmncr_base |= _4ECCEN | ECCPOS2 | ECCPOS_02; } else { chip->ecc.mode = NAND_ECC_SOFT; } @@ -819,13 +863,13 @@ static int __devinit flctl_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "failed to get I/O memory\n"); - goto err; + goto err_iomap; } flctl->reg = ioremap(res->start, resource_size(res)); if (flctl->reg == NULL) { dev_err(&pdev->dev, "failed to remap I/O memory\n"); - goto err; + goto err_iomap; } platform_set_drvdata(pdev, flctl); @@ -833,9 +877,9 @@ static int __devinit flctl_probe(struct platform_device *pdev) nand = &flctl->chip; flctl_mtd->priv = nand; flctl->pdev = pdev; + flctl->flcmncr_base = pdata->flcmncr_val; flctl->hwecc = pdata->has_hwecc; - - flctl_register_init(flctl, pdata->flcmncr_val); + flctl->holden = pdata->use_holden; nand->options = NAND_NO_AUTOINCR; @@ -855,23 +899,28 @@ static int __devinit flctl_probe(struct platform_device *pdev) nand->read_word = flctl_read_word; } + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + ret = nand_scan_ident(flctl_mtd, 1, NULL); if (ret) - goto err; + goto err_chip; ret = flctl_chip_init_tail(flctl_mtd); if (ret) - goto err; + goto err_chip; ret = nand_scan_tail(flctl_mtd); if (ret) - goto err; + goto err_chip; mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); return 0; -err: +err_chip: + pm_runtime_disable(&pdev->dev); +err_iomap: kfree(flctl); return ret; } @@ -881,6 +930,7 @@ static int __devexit flctl_remove(struct platform_device *pdev) struct sh_flctl *flctl = platform_get_drvdata(pdev); nand_release(&flctl->mtd); + pm_runtime_disable(&pdev->dev); kfree(flctl); return 0; diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index b175c0fd8b9..3421e3762a5 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -167,6 +167,7 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev) this->ecc.mode = NAND_ECC_HW; this->ecc.size = 256; this->ecc.bytes = 3; + this->ecc.strength = 1; this->badblock_pattern = data->badblock_pattern; this->ecc.layout = data->ecc_layout; this->ecc.hwctl = sharpsl_nand_enable_hwecc; @@ -181,8 +182,8 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev) /* Register the partitions */ sharpsl->mtd.name = "sharpsl-nand"; - err = mtd_device_parse_register(&sharpsl->mtd, NULL, 0, - data->partitions, data->nr_partitions); + err = mtd_device_parse_register(&sharpsl->mtd, NULL, NULL, + data->partitions, data->nr_partitions); if (err) goto err_add; diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 6caa0cd9d6a..5aa518081c5 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -430,6 +430,7 @@ static int tmio_probe(struct platform_device *dev) nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.size = 512; nand_chip->ecc.bytes = 6; + nand_chip->ecc.strength = 2; nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; nand_chip->ecc.calculate = tmio_nand_calculate_ecc; nand_chip->ecc.correct = tmio_nand_correct_data; @@ -456,9 +457,9 @@ static int tmio_probe(struct platform_device *dev) goto err_scan; } /* Register the partitions */ - retval = mtd_device_parse_register(mtd, NULL, 0, - data ? data->partition : NULL, - data ? data->num_partitions : 0); + retval = mtd_device_parse_register(mtd, NULL, NULL, + data ? data->partition : NULL, + data ? data->num_partitions : 0); if (!retval) return retval; diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index c7c4f1d11c7..26398dcf21c 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -356,6 +356,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; chip->chip_delay = 100; chip->controller = &drvdata->hw_control; @@ -386,7 +387,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) } mtd->name = txx9_priv->mtdname; - mtd_device_parse_register(mtd, NULL, 0, NULL, 0); + mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); drvdata->mtds[i] = mtd; } |