From 265dfbd9ae4388d582dd58e66aff24f4c30831ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 24 Mar 2013 21:53:24 +0100 Subject: mtd: bcm47xxsflash: implement ChipCommon R/W ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are needed for erasing/writing. Use a magic pointers and small functions to prepare code for adding other buses support in the future (like SSB). Signed-off-by: Rafał Miłecki Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 18e7761..d1f0dec 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -48,6 +48,17 @@ static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) * BCMA **************************************************/ +static int bcm47xxsflash_bcma_cc_read(struct bcm47xxsflash *b47s, u16 offset) +{ + return bcma_cc_read32(b47s->bcma_cc, offset); +} + +static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset, + u32 value) +{ + bcma_cc_write32(b47s->bcma_cc, offset, value); +} + static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) { struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); @@ -62,6 +73,8 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) sflash->priv = b47s; b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash); + b47s->cc_read = bcm47xxsflash_bcma_cc_read; + b47s->cc_write = bcm47xxsflash_bcma_cc_write; switch (b47s->bcma_cc->capabilities & BCMA_CC_CAP_FLASHT) { case BCMA_CC_FLASHT_STSER: diff --git a/drivers/mtd/devices/bcm47xxsflash.h b/drivers/mtd/devices/bcm47xxsflash.h index f22f8c4..fe93daf 100644 --- a/drivers/mtd/devices/bcm47xxsflash.h +++ b/drivers/mtd/devices/bcm47xxsflash.h @@ -60,6 +60,8 @@ enum bcm47xxsflash_type { struct bcm47xxsflash { struct bcma_drv_cc *bcma_cc; + int (*cc_read)(struct bcm47xxsflash *b47s, u16 offset); + void (*cc_write)(struct bcm47xxsflash *b47s, u16 offset, u32 value); enum bcm47xxsflash_type type; -- cgit v0.10.2 From bddcb5e7caa72890fcd1d6ee7803452f2295a407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 24 Mar 2013 21:51:31 +0100 Subject: mtd: bcm47xxsflash: implement polling chip status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index d1f0dec..2060856 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -12,6 +13,57 @@ MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); static const char * const probes[] = { "bcm47xxpart", NULL }; +/************************************************** + * Various helpers + **************************************************/ + +static void bcm47xxsflash_cmd(struct bcm47xxsflash *b47s, u32 opcode) +{ + int i; + + b47s->cc_write(b47s, BCMA_CC_FLASHCTL, BCMA_CC_FLASHCTL_START | opcode); + for (i = 0; i < 1000; i++) { + if (!(b47s->cc_read(b47s, BCMA_CC_FLASHCTL) & + BCMA_CC_FLASHCTL_BUSY)) + return; + cpu_relax(); + } + pr_err("Control command failed (timeout)!\n"); +} + +static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout) +{ + unsigned long deadline = jiffies + timeout; + + do { + switch (b47s->type) { + case BCM47XXSFLASH_TYPE_ST: + bcm47xxsflash_cmd(b47s, OPCODE_ST_RDSR); + if (!(b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & + SR_ST_WIP)) + return 0; + break; + case BCM47XXSFLASH_TYPE_ATMEL: + bcm47xxsflash_cmd(b47s, OPCODE_AT_STATUS); + if (b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & + SR_AT_READY) + return 0; + break; + } + + cpu_relax(); + udelay(1); + } while (!time_after_eq(jiffies, deadline)); + + pr_err("Timeout waiting for flash to be ready!\n"); + + return -EBUSY; +} + +/************************************************** + * MTD ops + **************************************************/ + static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -97,6 +149,9 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) goto err_dev_reg; } + if (bcm47xxsflash_poll(b47s, HZ / 10)) + pr_warn("Serial flash busy\n"); + return 0; err_dev_reg: -- cgit v0.10.2 From b966a40a3854e1263b3528ef95bc9e6017e8e515 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 28 Mar 2013 21:28:56 -0700 Subject: mtd: fsl_ifc_nand: remove incorrect kfree() The struct fsl_ifc_mtd is allocated with devm_kzalloc, so its memory is "managed" automatically by the kernel. That is, we do not need to free it explicitly; it will be freed when the device is removed. And we *certainly* shouldn't free it with a regular kfree(). Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index f1f7f12..180bfa7 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -908,7 +908,6 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) ifc_nand_ctrl->chips[priv->bank] = NULL; dev_set_drvdata(priv->dev, NULL); - kfree(priv); return 0; } -- cgit v0.10.2 From c88fd9d9705506b5d75757735745183e5f2ff2cd Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Sat, 23 Mar 2013 14:07:47 +0100 Subject: mtd: bcm63xxpart: use size macro for CFE block size Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c index 6eeb84c..fc3d7d0 100644 --- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/bcm63xxpart.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,7 @@ #define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */ -#define BCM63XX_CFE_BLOCK_SIZE 0x10000 /* always at least 64KiB */ +#define BCM63XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */ #define BCM63XX_CFE_MAGIC_OFFSET 0x4e0 -- cgit v0.10.2 From e60665c50f0d94c0f2696b19cad7d281b2bc00cf Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Sat, 23 Mar 2013 14:07:48 +0100 Subject: MIPS: BCM63XX: export PSI size from nvram Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/arch/mips/bcm63xx/nvram.c b/arch/mips/bcm63xx/nvram.c index e652e57..4b50d40 100644 --- a/arch/mips/bcm63xx/nvram.c +++ b/arch/mips/bcm63xx/nvram.c @@ -35,6 +35,8 @@ struct bcm963xx_nvram { u32 checksum_high; }; +#define BCM63XX_DEFAULT_PSI_SIZE 64 + static struct bcm963xx_nvram nvram; static int mac_addr_used; @@ -114,3 +116,12 @@ int bcm63xx_nvram_get_mac_address(u8 *mac) return 0; } EXPORT_SYMBOL(bcm63xx_nvram_get_mac_address); + +int bcm63xx_nvram_get_psi_size(void) +{ + if (nvram.psi_size > 0) + return nvram.psi_size; + + return BCM63XX_DEFAULT_PSI_SIZE; +} +EXPORT_SYMBOL(bcm63xx_nvram_get_psi_size); diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_nvram.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_nvram.h index 4e0b6bc..348df49 100644 --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_nvram.h +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_nvram.h @@ -30,4 +30,6 @@ u8 *bcm63xx_nvram_get_name(void); */ int bcm63xx_nvram_get_mac_address(u8 *mac); +int bcm63xx_nvram_get_psi_size(void); + #endif /* BCM63XX_NVRAM_H */ -- cgit v0.10.2 From d085eea16c05e5fecf258fc254059e9bc1e6db91 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Sat, 23 Mar 2013 14:07:49 +0100 Subject: mtd: bcm63xxpart: use nvram for PSI size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read out the SPI size from nvram instead of defaulting to 64KiB — some vendors actually use values larger than the "max" value of 64. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c index fc3d7d0..5c81390 100644 --- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/bcm63xxpart.c @@ -4,7 +4,7 @@ * Copyright © 2006-2008 Florian Fainelli * Mike Albon * Copyright © 2009-2010 Daniel Dickinson - * Copyright © 2011-2012 Jonas Gorski + * Copyright © 2011-2013 Jonas Gorski * * 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 @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -91,7 +92,8 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, BCM63XX_CFE_BLOCK_SIZE); cfelen = cfe_erasesize; - nvramlen = cfe_erasesize; + nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K; + nvramlen = roundup(nvramlen, cfe_erasesize); /* Allocate memory for buffer */ buf = vmalloc(sizeof(struct bcm_tag)); -- cgit v0.10.2 From b87c92c1ad6c29c4068a6d64483903a89ec2bce1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 26 Mar 2013 15:53:04 +0900 Subject: mtd: r852: add CONFIG_PM_SLEEP to suspend/resume functions Add CONFIG_PM_SLEEP to suspend/resume functions to fix the following build warning when CONFIG_PM_SLEEP is not selected. This is because sleep PM callbacks defined by SIMPLE_DEV_PM_OPS are only used when the CONFIG_PM_SLEEP is enabled. Also, unnecessary NULL defines are removed. drivers/mtd/nand/r852.c:1006:12: warning: 'r852_suspend' defined but not used [-Wunused-function] drivers/mtd/nand/r852.c:1027:12: warning: 'r852_resume' defined but not used [-Wunused-function] Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 4495f85..ef72884 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -1002,7 +1002,7 @@ void r852_shutdown(struct pci_dev *pci_dev) pci_disable_device(pci_dev); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int r852_suspend(struct device *device) { struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); @@ -1055,9 +1055,6 @@ static int r852_resume(struct device *device) r852_update_card_detect(dev); return 0; } -#else -#define r852_suspend NULL -#define r852_resume NULL #endif static const struct pci_device_id r852_pci_id_tbl[] = { -- cgit v0.10.2 From 80ce4dde2f4fa75cec71790d8f582a4ca00dbdb0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 26 Mar 2013 15:53:48 +0900 Subject: mtd: fsmc_nand: add CONFIG_PM_SLEEP to suspend/resume functions Add CONFIG_PM_SLEEP to suspend/resume functions to fix the following build warning when CONFIG_PM_SLEEP is not selected. This is because sleep PM callbacks defined by SIMPLE_DEV_PM_OPS are only used when the CONFIG_PM_SLEEP is enabled. Also, unnecessary CONFIG_PM ifdefs are removed. drivers/mtd/nand/fsmc_nand.c:1194:12: warning: 'fsmc_nand_suspend' defined but not used [-Wunused-function] drivers/mtd/nand/fsmc_nand.c:1202:12: warning: 'fsmc_nand_resume' defined but not used [-Wunused-function] Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 911e243..0e5a1d9 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -1190,7 +1190,7 @@ static int fsmc_nand_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int fsmc_nand_suspend(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); @@ -1210,9 +1210,9 @@ static int fsmc_nand_resume(struct device *dev) } return 0; } +#endif 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[] = { @@ -1229,9 +1229,7 @@ static struct platform_driver fsmc_nand_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 }, }; -- cgit v0.10.2 From aba98cfde97fe84667204b038c6511d30d560c8c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 26 Mar 2013 15:54:22 +0900 Subject: mtd: spear_smi: add CONFIG_PM_SLEEP to suspend/resume functions Add CONFIG_PM_SLEEP to suspend/resume functions to fix the following build warning when CONFIG_PM_SLEEP is not selected. This is because sleep PM callbacks defined by SIMPLE_DEV_PM_OPS are only used when the CONFIG_PM_SLEEP is enabled. Also, unnecessary CONFIG_PM ifdefs are removed drivers/mtd/devices/spear_smi.c:1049:12: warning: 'spear_smi_suspend' defined but not used [-Wunused-function] drivers/mtd/devices/spear_smi.c:1059:12: warning: 'spear_smi_resume' defined but not used [-Wunused-function] Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 8a82b8b..0e1c22e 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -1045,7 +1045,7 @@ static int spear_smi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int spear_smi_suspend(struct device *dev) { struct spear_smi *sdev = dev_get_drvdata(dev); @@ -1068,9 +1068,9 @@ static int spear_smi_resume(struct device *dev) spear_smi_hw_init(sdev); return ret; } +#endif static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume); -#endif #ifdef CONFIG_OF static const struct of_device_id spear_smi_id_table[] = { @@ -1086,9 +1086,7 @@ static struct platform_driver spear_smi_driver = { .bus = &platform_bus_type, .owner = THIS_MODULE, .of_match_table = of_match_ptr(spear_smi_id_table), -#ifdef CONFIG_PM .pm = &spear_smi_pm_ops, -#endif }, .probe = spear_smi_probe, .remove = spear_smi_remove, -- cgit v0.10.2 From 25983b183235ab84d8c5550eae75d0d5006828d0 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 1 Apr 2013 17:40:18 +0800 Subject: mtd: cfi_cmdset_0002: print @adr when write timeout occurs Print out the @adr when the write timeout occurs. This is useful to check if the write timeouts occur at the same address. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index fff665d..89b9d68 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -1571,8 +1571,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, xip_enable(map, chip, adr); /* FIXME - should have reset delay before continuing */ - printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); + printk(KERN_WARNING "MTD %s(): software timeout, address:0x%.8lx.\n", + __func__, adr); ret = -EIO; op_done: -- cgit v0.10.2 From 1594908ac06509bf0768d78e66f4138617fd7314 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 6 Apr 2013 15:40:30 +0900 Subject: mtd: sst25l: use spi_get_drvdata() and spi_set_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index 8091b01..38b5979 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -370,7 +370,7 @@ static int sst25l_probe(struct spi_device *spi) flash->spi = spi; mutex_init(&flash->lock); - dev_set_drvdata(&spi->dev, flash); + spi_set_drvdata(spi, flash); data = spi->dev.platform_data; if (data && data->name) @@ -404,7 +404,7 @@ static int sst25l_probe(struct spi_device *spi) data ? data->nr_parts : 0); if (ret) { kfree(flash); - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); return -ENODEV; } @@ -413,7 +413,7 @@ static int sst25l_probe(struct spi_device *spi) static int sst25l_remove(struct spi_device *spi) { - struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); + struct sst25l_flash *flash = spi_get_drvdata(spi); int ret; ret = mtd_device_unregister(&flash->mtd); -- cgit v0.10.2 From 5cacbfa9fe263b7f65a8efdf68bf14754c3d250a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 6 Apr 2013 15:41:03 +0900 Subject: mtd: dataflash: use spi_get_drvdata() and spi_set_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 28779b6..4a43664 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -661,7 +661,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n", name, (long long)((device->size + 1023) >> 10), pagesize, otp_tag); - dev_set_drvdata(&spi->dev, priv); + spi_set_drvdata(spi, priv); ppdata.of_node = spi->dev.of_node; err = mtd_device_parse_register(device, NULL, &ppdata, @@ -671,7 +671,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, if (!err) return 0; - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); kfree(priv); return err; } @@ -895,14 +895,14 @@ static int dataflash_probe(struct spi_device *spi) static int dataflash_remove(struct spi_device *spi) { - struct dataflash *flash = dev_get_drvdata(&spi->dev); + struct dataflash *flash = spi_get_drvdata(spi); int status; pr_debug("%s: remove\n", dev_name(&spi->dev)); status = mtd_device_unregister(&flash->mtd); if (status == 0) { - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); kfree(flash); } return status; -- cgit v0.10.2 From 975aefc9de7ab893e9e784ec52cf97e1827259da Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 6 Apr 2013 15:41:32 +0900 Subject: mtd: m25p80: use spi_get_drvdata() and spi_set_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 2f3d2a5..d2232e7 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -972,7 +972,7 @@ static int m25p_probe(struct spi_device *spi) flash->spi = spi; mutex_init(&flash->lock); - dev_set_drvdata(&spi->dev, flash); + spi_set_drvdata(spi, flash); /* * Atmel, SST and Intel/Numonyx serial flash tend to power @@ -1080,7 +1080,7 @@ static int m25p_probe(struct spi_device *spi) static int m25p_remove(struct spi_device *spi) { - struct m25p *flash = dev_get_drvdata(&spi->dev); + struct m25p *flash = spi_get_drvdata(spi); int status; /* Clean up MTD stuff. */ -- cgit v0.10.2 From 435796889d714ffaf90923cfea868d8b5d28008c Mon Sep 17 00:00:00 2001 From: Silviu-Mihai Popescu Date: Thu, 11 Apr 2013 00:01:51 +0300 Subject: mtd: devices: convert OMAP BCH to devm_ioremap_resource() Convert all users of devm_request_and_ioremap() to the newly introduced devm_ioremap_resource() which provides more consistent error handling. Signed-off-by: Silviu-Mihai Popescu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index dccef9f..fc8ad9f 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -351,9 +351,9 @@ static int elm_probe(struct platform_device *pdev) return -ENODEV; } - info->elm_base = devm_request_and_ioremap(&pdev->dev, res); - if (!info->elm_base) - return -EADDRNOTAVAIL; + info->elm_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->elm_base)) + return PTR_ERR(info->elm_base); ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, pdev->name, info); -- cgit v0.10.2 From 20cd0008bb01ea0f5dd5779896bf6f069aab9c8a Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 10 Apr 2013 17:34:37 -0500 Subject: mtd: fsl_ifc_nand: set NAND_NO_SUBPAGE_WRITE This controller only does ECC on full-page accesses, even though the ECC consists of multiple steps. fsl_elbc_nand can get away with this because the ECC of an all-0xff region will be all-0xff, but this is not true with the ECC algorithms used by IFC. Signed-off-by: Scott Wood Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 180bfa7..317a771 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -823,7 +823,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* set up nand options */ chip->bbt_options = NAND_BBT_USE_FLASH; - + chip->options = NAND_NO_SUBPAGE_WRITE; if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { chip->read_byte = fsl_ifc_read_byte16; -- cgit v0.10.2 From 87c9511fba2bd069a35e1312587a29e112fc0cd6 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 11 Apr 2013 01:34:57 -0700 Subject: mtd: m25p80: utilize dedicated 4-byte addressing commands Traditionally, the command set used by SPI flash only supported a 3-byte address. However, large SPI flash (>= 32MiB, or 256Mib) require 4 bytes to address the entire flash. Most manufacturers have supplied a mode switch (via a "bank register writer", or a "enable 4-byte mode" command), which tells the flash to expect 4 address cycles from now on, instead of 3. This mode remains until power is cut, the reset line is triggered (on packages where present), or a command is sent to reset the flash or to reset the 3-byte addressing mode. As an alternative, some flash manufacturers have developed a new command set that accept a full 4-byte address. They can be used orthogonally to any of the modes; that is, they can be used when the flash is in either 3-byte or 4-byte address mode. Now, there are a number of reasons why the "stateful" 4-byte address mode switch may not be acceptable. For instance, some SoC's perform a dumb boot sequence in which they only send 3-byte read commands to the flash. However, if an unexpected reset occurs, the flash chip cannot be guaranteed to return to its 3-byte mode. Thus, the SoC controller and flash will not understand each other. (One might consider hooking up the aforementioned reset pin to the system reset line so that any system reset will reset the flash to 3-byte mode, but some packages do not provide this pin. And in some other packages, one must choose between having a reset pin and having enough pins for 4-output QSPI support. It is an error prone process choosing a flash that will support a hardware reset pin!) This patch provides support for the new stateless command set, so that we can avoid the problems that come with a stateful addressing mode change. The flash can be left in "3-byte mode" while still accessing the entire flash. Note that Spansion supports this command set on all its large flash (e.g, S25FL512S), and Macronix has begun supporting this command set on some new flash (e.g., MX25L25635F). For the moment, I don't know how to differentiate the Macronix that don't support this command set (e.g., MX25L25635E) from those that do, so this patch only supports Spansion. Signed-off-by: Brian Norris Acked-by: Marek Vasut Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index d2232e7..2db1ee4 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -48,6 +48,12 @@ #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ +/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ +#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ + /* Used for SST flashes only. */ #define OPCODE_BP 0x02 /* Byte program */ #define OPCODE_WRDI 0x04 /* Write disable */ @@ -84,6 +90,8 @@ struct m25p { u16 page_size; u16 addr_width; u8 erase_opcode; + u8 read_opcode; + u8 program_opcode; u8 *command; bool fast_read; }; @@ -371,7 +379,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, */ /* Set up the write data buffer. */ - opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ; + opcode = flash->read_opcode; flash->command[0] = opcode; m25p_addr2cmd(flash, from, flash->command); @@ -422,7 +430,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, write_enable(flash); /* Set up the opcode in the write buffer. */ - flash->command[0] = OPCODE_PP; + flash->command[0] = flash->program_opcode; m25p_addr2cmd(flash, to, flash->command); page_offset = to & (flash->page_size - 1); @@ -1037,15 +1045,32 @@ static int m25p_probe(struct spi_device *spi) flash->fast_read = true; #endif + /* Default commands */ + if (flash->fast_read) + flash->read_opcode = OPCODE_FAST_READ; + else + flash->read_opcode = OPCODE_NORM_READ; + + flash->program_opcode = OPCODE_PP; + if (info->addr_width) flash->addr_width = info->addr_width; - else { + else if (flash->mtd.size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ - if (flash->mtd.size > 0x1000000) { - flash->addr_width = 4; - set_4byte(flash, info->jedec_id, 1); + flash->addr_width = 4; + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { + /* Dedicated 4-byte command set */ + flash->read_opcode = flash->fast_read ? + OPCODE_FAST_READ_4B : + OPCODE_NORM_READ_4B; + flash->program_opcode = OPCODE_PP_4B; + /* No small sector erase for 4-byte command set */ + flash->erase_opcode = OPCODE_SE_4B; + flash->mtd.erasesize = info->sector_size; } else - flash->addr_width = 3; + set_4byte(flash, info->jedec_id, 1); + } else { + flash->addr_width = 3; } dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, -- cgit v0.10.2 From caddab0f5b7563db72640e6c6a21fd7076418f36 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 11 Apr 2013 01:34:58 -0700 Subject: mtd: m25p80: correct EN4B/EX4B comment Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 2db1ee4..d3b94b0 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -59,7 +59,7 @@ #define OPCODE_WRDI 0x04 /* Write disable */ #define OPCODE_AAI_WP 0xad /* Auto address increment word program */ -/* Used for Macronix flashes only. */ +/* Used for Macronix and Winbond flashes. */ #define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ #define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ -- cgit v0.10.2 From ce157510eb886feb996a7a13354973bdf4aae1a1 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 11 Apr 2013 01:34:59 -0700 Subject: mtd: nand: reword nand_chip bad block interface comments This remedies a few problems: (1) The use of "the" vs. "a" is a little confusing, IMO. (2) nand_chip.block_bad is used exclusively for checking the OOB bad block markers of a NAND. Any BBT functionality is handled in nand_bbt.c, so this description should differentiate itself from nand_bbt.c. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ab63634..50c5ea0 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -390,8 +390,8 @@ struct nand_buffers { * @write_buf: [REPLACEABLE] write data from the buffer to the chip * @read_buf: [REPLACEABLE] read data from the chip into the buffer * @select_chip: [REPLACEABLE] select chip nr - * @block_bad: [REPLACEABLE] check, if the block is bad - * @block_markbad: [REPLACEABLE] mark the block bad + * @block_bad: [REPLACEABLE] check if a block is bad, using OOB markers + * @block_markbad: [REPLACEABLE] mark a block bad * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling * ALE/CLE/nCE. Also used to write command and address * @init_size: [BOARDSPECIFIC] hardwarespecific function for setting -- cgit v0.10.2 From 283df420fc3bf9294149fdc9173709feafde4222 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 6 May 2013 17:53:48 +0400 Subject: mtd: nand-gpio: Convert driver to using resource-managed functions Patch converts driver to using resource-managed functions. Signed-off-by: Alexander Shiyan Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 89065dd..c35f46e 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -233,119 +234,81 @@ gpio_nand_get_io_sync(struct platform_device *pdev) static int gpio_nand_remove(struct platform_device *dev) { struct gpiomtd *gpiomtd = platform_get_drvdata(dev); - struct resource *res; nand_release(&gpiomtd->mtd_info); - res = gpio_nand_get_io_sync(dev); - iounmap(gpiomtd->io_sync); - if (res) - release_mem_region(res->start, resource_size(res)); - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res->start, resource_size(res)); - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); gpio_set_value(gpiomtd->plat.gpio_nce, 1); - gpio_free(gpiomtd->plat.gpio_cle); - gpio_free(gpiomtd->plat.gpio_ale); - gpio_free(gpiomtd->plat.gpio_nce); - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_free(gpiomtd->plat.gpio_nwp); - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) - gpio_free(gpiomtd->plat.gpio_rdy); - return 0; } -static void __iomem *request_and_remap(struct resource *res, size_t size, - const char *name, int *err) -{ - void __iomem *ptr; - - if (!request_mem_region(res->start, resource_size(res), name)) { - *err = -EBUSY; - return NULL; - } - - ptr = ioremap(res->start, size); - if (!ptr) { - release_mem_region(res->start, resource_size(res)); - *err = -ENOMEM; - } - return ptr; -} - static int gpio_nand_probe(struct platform_device *dev) { struct gpiomtd *gpiomtd; struct nand_chip *this; - struct resource *res0, *res1; + struct resource *res; struct mtd_part_parser_data ppdata = {}; int ret = 0; if (!dev->dev.of_node && !dev->dev.platform_data) return -EINVAL; - res0 = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res0) - return -EINVAL; - gpiomtd = devm_kzalloc(&dev->dev, sizeof(*gpiomtd), GFP_KERNEL); - if (gpiomtd == NULL) { + if (!gpiomtd) { dev_err(&dev->dev, "failed to create NAND MTD\n"); return -ENOMEM; } this = &gpiomtd->nand_chip; - this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret); - if (!this->IO_ADDR_R) { - dev_err(&dev->dev, "unable to map NAND\n"); - goto err_map; - } - res1 = gpio_nand_get_io_sync(dev); - if (res1) { - gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret); - if (!gpiomtd->io_sync) { - dev_err(&dev->dev, "unable to map sync NAND\n"); - goto err_sync; - } + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + this->IO_ADDR_R = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(this->IO_ADDR_R)) + return PTR_ERR(this->IO_ADDR_R); + + res = gpio_nand_get_io_sync(dev); + if (res) { + gpiomtd->io_sync = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(gpiomtd->io_sync)) + return PTR_ERR(gpiomtd->io_sync); } ret = gpio_nand_get_config(&dev->dev, &gpiomtd->plat); if (ret) - goto err_nce; + return ret; - ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE"); + ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_nce, "NAND NCE"); if (ret) - goto err_nce; + return ret; gpio_direction_output(gpiomtd->plat.gpio_nce, 1); + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { - ret = gpio_request(gpiomtd->plat.gpio_nwp, "NAND NWP"); + ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_nwp, + "NAND NWP"); if (ret) - goto err_nwp; - gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); + return ret; } - ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE"); + + ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_ale, "NAND ALE"); if (ret) - goto err_ale; + return ret; gpio_direction_output(gpiomtd->plat.gpio_ale, 0); - ret = gpio_request(gpiomtd->plat.gpio_cle, "NAND CLE"); + + ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_cle, "NAND CLE"); if (ret) - goto err_cle; + return ret; gpio_direction_output(gpiomtd->plat.gpio_cle, 0); + if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { - ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY"); + ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_rdy, + "NAND RDY"); if (ret) - goto err_rdy; + return ret; gpio_direction_input(gpiomtd->plat.gpio_rdy); } - this->IO_ADDR_W = this->IO_ADDR_R; this->ecc.mode = NAND_ECC_SOFT; this->options = gpiomtd->plat.options; @@ -367,8 +330,12 @@ static int gpio_nand_probe(struct platform_device *dev) gpiomtd->mtd_info.priv = this; gpiomtd->mtd_info.owner = THIS_MODULE; + platform_set_drvdata(dev, gpiomtd); + + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) + gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); + if (nand_scan(&gpiomtd->mtd_info, 1)) { - dev_err(&dev->dev, "no nand chips found?\n"); ret = -ENXIO; goto err_wp; } @@ -381,34 +348,13 @@ static int gpio_nand_probe(struct platform_device *dev) ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata, gpiomtd->plat.parts, gpiomtd->plat.num_parts); - if (ret) - goto err_wp; - platform_set_drvdata(dev, gpiomtd); - - return 0; + if (!ret) + return 0; err_wp: if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) - gpio_free(gpiomtd->plat.gpio_rdy); -err_rdy: - gpio_free(gpiomtd->plat.gpio_cle); -err_cle: - gpio_free(gpiomtd->plat.gpio_ale); -err_ale: - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_free(gpiomtd->plat.gpio_nwp); -err_nwp: - gpio_free(gpiomtd->plat.gpio_nce); -err_nce: - iounmap(gpiomtd->io_sync); - if (res1) - release_mem_region(res1->start, resource_size(res1)); -err_sync: - iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res0->start, resource_size(res0)); -err_map: + return ret; } -- cgit v0.10.2 From c85d32d56859bb01f775e12771150dc47a7b03ba Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 6 May 2013 17:53:49 +0400 Subject: mtd: nand-gpio: Use default dev_ready function if RDY is missing in configuration Signed-off-by: Alexander Shiyan Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index c35f46e..8f102d1 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -136,10 +136,7 @@ static int gpio_nand_devready(struct mtd_info *mtd) { struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) - return gpio_get_value(gpiomtd->plat.gpio_rdy); - - return 1; + return gpio_get_value(gpiomtd->plat.gpio_rdy); } #ifdef CONFIG_OF @@ -307,6 +304,7 @@ static int gpio_nand_probe(struct platform_device *dev) if (ret) return ret; gpio_direction_input(gpiomtd->plat.gpio_rdy); + this->dev_ready = gpio_nand_devready; } this->IO_ADDR_W = this->IO_ADDR_R; @@ -316,7 +314,6 @@ static int gpio_nand_probe(struct platform_device *dev) /* install our routines */ this->cmd_ctrl = gpio_nand_cmd_ctrl; - this->dev_ready = gpio_nand_devready; if (this->options & NAND_BUSWIDTH_16) { this->read_buf = gpio_nand_readbuf16; -- cgit v0.10.2 From ee4f36663b2a3626a9dadb2679a0ddcff1371891 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 6 May 2013 17:53:50 +0400 Subject: mtd: nand-gpio: Do not override GPIOs if driver uses platform_data but OF is enabled in kernel config Signed-off-by: Alexander Shiyan Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 8f102d1..0d26f8b 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -151,6 +151,9 @@ static int gpio_nand_get_config_of(const struct device *dev, { u32 val; + if (!dev->of_node) + return -ENODEV; + if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { if (val == 2) { plat->options |= NAND_BUSWIDTH_16; -- cgit v0.10.2 From 2189408cb7ffd221075d3f414241da35dc30ae12 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 6 May 2013 17:53:51 +0400 Subject: mtd: nand-gpio: Use default nand_base {read/write}_buf functions Signed-off-by: Alexander Shiyan Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 0d26f8b..312c285 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -87,51 +87,6 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) gpio_nand_dosync(gpiomtd); } -static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - - iowrite8_rep(this->IO_ADDR_W, buf, len); -} - -static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - - ioread8_rep(this->IO_ADDR_R, buf, len); -} - -static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, - int len) -{ - struct nand_chip *this = mtd->priv; - - if (IS_ALIGNED((unsigned long)buf, 2)) { - iowrite16_rep(this->IO_ADDR_W, buf, len>>1); - } else { - int i; - unsigned short *ptr = (unsigned short *)buf; - - for (i = 0; i < len; i += 2, ptr++) - writew(*ptr, this->IO_ADDR_W); - } -} - -static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - - if (IS_ALIGNED((unsigned long)buf, 2)) { - ioread16_rep(this->IO_ADDR_R, buf, len>>1); - } else { - int i; - unsigned short *ptr = (unsigned short *)buf; - - for (i = 0; i < len; i += 2, ptr++) - *ptr = readw(this->IO_ADDR_R); - } -} - static int gpio_nand_devready(struct mtd_info *mtd) { struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); @@ -314,19 +269,8 @@ static int gpio_nand_probe(struct platform_device *dev) this->ecc.mode = NAND_ECC_SOFT; this->options = gpiomtd->plat.options; this->chip_delay = gpiomtd->plat.chip_delay; - - /* install our routines */ this->cmd_ctrl = gpio_nand_cmd_ctrl; - if (this->options & NAND_BUSWIDTH_16) { - this->read_buf = gpio_nand_readbuf16; - this->write_buf = gpio_nand_writebuf16; - } else { - this->read_buf = gpio_nand_readbuf; - this->write_buf = gpio_nand_writebuf; - } - - /* set the mtd private data for the nand driver */ gpiomtd->mtd_info.priv = this; gpiomtd->mtd_info.owner = THIS_MODULE; -- cgit v0.10.2 From ba44039816367e9eebc3c0077ff1f25e95a73c26 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 6 May 2013 17:53:52 +0400 Subject: mtd: nand-gpio: Unneeded dependency on ARM removed Signed-off-by: Alexander Shiyan Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 50543f1..5ef8f5e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -75,7 +75,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR config MTD_NAND_GPIO tristate "GPIO NAND Flash driver" - depends on GPIOLIB && ARM + depends on GPIOLIB help This enables a GPIO based NAND flash driver. -- cgit v0.10.2 From f8e81c2b496d2ab85322feb4f0b581bef63cbff0 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 6 May 2013 17:53:53 +0400 Subject: mtd: nand-gpio: Rename internal variables to match functionality struct platform_device *dev => pdev struct nand_chip *this => chip Signed-off-by: Alexander Shiyan Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 312c285..6bf8f2e 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -186,9 +186,9 @@ gpio_nand_get_io_sync(struct platform_device *pdev) return platform_get_resource(pdev, IORESOURCE_MEM, 1); } -static int gpio_nand_remove(struct platform_device *dev) +static int gpio_nand_remove(struct platform_device *pdev) { - struct gpiomtd *gpiomtd = platform_get_drvdata(dev); + struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); nand_release(&gpiomtd->mtd_info); @@ -199,82 +199,82 @@ static int gpio_nand_remove(struct platform_device *dev) return 0; } -static int gpio_nand_probe(struct platform_device *dev) +static int gpio_nand_probe(struct platform_device *pdev) { struct gpiomtd *gpiomtd; - struct nand_chip *this; + struct nand_chip *chip; struct resource *res; struct mtd_part_parser_data ppdata = {}; int ret = 0; - if (!dev->dev.of_node && !dev->dev.platform_data) + if (!pdev->dev.of_node && !pdev->dev.platform_data) return -EINVAL; - gpiomtd = devm_kzalloc(&dev->dev, sizeof(*gpiomtd), GFP_KERNEL); + gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL); if (!gpiomtd) { - dev_err(&dev->dev, "failed to create NAND MTD\n"); + dev_err(&pdev->dev, "failed to create NAND MTD\n"); return -ENOMEM; } - this = &gpiomtd->nand_chip; + chip = &gpiomtd->nand_chip; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - this->IO_ADDR_R = devm_ioremap_resource(&dev->dev, res); - if (IS_ERR(this->IO_ADDR_R)) - return PTR_ERR(this->IO_ADDR_R); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(chip->IO_ADDR_R)) + return PTR_ERR(chip->IO_ADDR_R); - res = gpio_nand_get_io_sync(dev); + res = gpio_nand_get_io_sync(pdev); if (res) { - gpiomtd->io_sync = devm_ioremap_resource(&dev->dev, res); + gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(gpiomtd->io_sync)) return PTR_ERR(gpiomtd->io_sync); } - ret = gpio_nand_get_config(&dev->dev, &gpiomtd->plat); + ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat); if (ret) return ret; - ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_nce, "NAND NCE"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE"); if (ret) return ret; gpio_direction_output(gpiomtd->plat.gpio_nce, 1); if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { - ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_nwp, + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp, "NAND NWP"); if (ret) return ret; } - ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_ale, "NAND ALE"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE"); if (ret) return ret; gpio_direction_output(gpiomtd->plat.gpio_ale, 0); - ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_cle, "NAND CLE"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE"); if (ret) return ret; gpio_direction_output(gpiomtd->plat.gpio_cle, 0); if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { - ret = devm_gpio_request(&dev->dev, gpiomtd->plat.gpio_rdy, + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy, "NAND RDY"); if (ret) return ret; gpio_direction_input(gpiomtd->plat.gpio_rdy); - this->dev_ready = gpio_nand_devready; + chip->dev_ready = gpio_nand_devready; } - this->IO_ADDR_W = this->IO_ADDR_R; - this->ecc.mode = NAND_ECC_SOFT; - this->options = gpiomtd->plat.options; - this->chip_delay = gpiomtd->plat.chip_delay; - this->cmd_ctrl = gpio_nand_cmd_ctrl; + chip->IO_ADDR_W = chip->IO_ADDR_R; + chip->ecc.mode = NAND_ECC_SOFT; + chip->options = gpiomtd->plat.options; + chip->chip_delay = gpiomtd->plat.chip_delay; + chip->cmd_ctrl = gpio_nand_cmd_ctrl; - gpiomtd->mtd_info.priv = this; - gpiomtd->mtd_info.owner = THIS_MODULE; + gpiomtd->mtd_info.priv = chip; + gpiomtd->mtd_info.owner = THIS_MODULE; - platform_set_drvdata(dev, gpiomtd); + platform_set_drvdata(pdev, gpiomtd); if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); @@ -288,7 +288,7 @@ static int gpio_nand_probe(struct platform_device *dev) gpiomtd->plat.adjust_parts(&gpiomtd->plat, gpiomtd->mtd_info.size); - ppdata.of_node = dev->dev.of_node; + ppdata.of_node = pdev->dev.of_node; ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata, gpiomtd->plat.parts, gpiomtd->plat.num_parts); -- cgit v0.10.2 From 65c972afb572e74a1b394baead89b934ca15f12b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 6 May 2013 17:53:54 +0400 Subject: mtd: nand-gpio: Add missing "owner" field in platform_driver struct Signed-off-by: Alexander Shiyan Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 6bf8f2e..800a1cc 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -307,6 +307,7 @@ static struct platform_driver gpio_nand_driver = { .remove = gpio_nand_remove, .driver = { .name = "gpio-nand", + .owner = THIS_MODULE, .of_match_table = of_match_ptr(gpio_nand_id_table), }, }; -- cgit v0.10.2 From 76413839dbf3f03a3024c4c206d7098edbf0f83c Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 13 Apr 2013 09:32:13 +0400 Subject: mtd: nand_base: Use io{read, write}*_rep functions for transfer This patch replaces the usage of loops in the nand_base code with io{read,write}{8,16}_rep calls instead. Signed-off-by: Alexander Shiyan Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dfcd0a5..edc7663 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -211,11 +211,9 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr) */ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; - for (i = 0; i < len; i++) - writeb(buf[i], chip->IO_ADDR_W); + iowrite8_rep(chip->IO_ADDR_W, buf, len); } /** @@ -228,11 +226,9 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) */ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; - for (i = 0; i < len; i++) - buf[i] = readb(chip->IO_ADDR_R); + ioread8_rep(chip->IO_ADDR_R, buf, len); } /** @@ -245,14 +241,10 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) */ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; - len >>= 1; - - for (i = 0; i < len; i++) - writew(p[i], chip->IO_ADDR_W); + iowrite16_rep(chip->IO_ADDR_W, p, len >> 1); } /** @@ -265,13 +257,10 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) */ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; - len >>= 1; - for (i = 0; i < len; i++) - p[i] = readw(chip->IO_ADDR_R); + ioread16_rep(chip->IO_ADDR_R, p, len >> 1); } /** -- cgit v0.10.2 From 4b6ff7afe8ca356ac51e98a7e01997cc17669254 Mon Sep 17 00:00:00 2001 From: Girish K S Date: Tue, 16 Apr 2013 14:01:14 +0530 Subject: mtd: devices: added the 16MiB winbond device Added a 16MiB winbond devce to the device list erase size = 64KiB and number of blocks = 256. Signed-off-by: Girish K S Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index d3b94b0..57522c8 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -848,6 +848,7 @@ static const struct spi_device_id m25p_ids[] = { { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, -- cgit v0.10.2 From a1c06609b37c304c58f6d1d3b8e987edaf3391f3 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 19 Apr 2013 10:59:35 -0700 Subject: mtd: Convert logging messages Use a more current logging style. Convert homegrown ERROR/INFO macros to pr_. Convert homegrown parse_err macros to pr_err and expand hidden flow control. Signed-off-by: Joe Perches Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index e081bfe..5cb4c04 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -6,6 +6,9 @@ * * Licence: GPL */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -18,10 +21,6 @@ #include #include -#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) -#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) - - /* Info for the block device */ struct block2mtd_dev { struct list_head list; @@ -84,7 +83,7 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr) err = _block2mtd_erase(dev, from, len); mutex_unlock(&dev->write_mutex); if (err) { - ERROR("erase failed err = %d", err); + pr_err("erase failed err = %d\n", err); instr->state = MTD_ERASE_FAILED; } else instr->state = MTD_ERASE_DONE; @@ -239,13 +238,13 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) #endif if (IS_ERR(bdev)) { - ERROR("error: cannot open device %s", devname); + pr_err("error: cannot open device %s\n", devname); goto devinit_err; } dev->blkdev = bdev; if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { - ERROR("attempting to use an MTD device as a block device"); + pr_err("attempting to use an MTD device as a block device\n"); goto devinit_err; } @@ -277,9 +276,10 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) goto devinit_err; } list_add(&dev->list, &blkmtd_device_list); - INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, - dev->mtd.name + strlen("block2mtd: "), - dev->mtd.erasesize >> 10, dev->mtd.erasesize); + pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n", + dev->mtd.index, + dev->mtd.name + strlen("block2mtd: "), + dev->mtd.erasesize >> 10, dev->mtd.erasesize); return dev; devinit_err: @@ -339,17 +339,11 @@ static inline void kill_final_newline(char *str) } -#define parse_err(fmt, args...) do { \ - ERROR(fmt, ## args); \ - return 0; \ -} while (0) - #ifndef MODULE static int block2mtd_init_called = 0; static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */ #endif - static int block2mtd_setup2(const char *val) { char buf[80 + 12]; /* 80 for device, 12 for erase size */ @@ -359,8 +353,10 @@ static int block2mtd_setup2(const char *val) size_t erase_size = PAGE_SIZE; int i, ret; - if (strnlen(val, sizeof(buf)) >= sizeof(buf)) - parse_err("parameter too long"); + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) { + pr_err("parameter too long\n"); + return 0; + } strcpy(str, val); kill_final_newline(str); @@ -368,20 +364,27 @@ static int block2mtd_setup2(const char *val) for (i = 0; i < 2; i++) token[i] = strsep(&str, ","); - if (str) - parse_err("too many arguments"); + if (str) { + pr_err("too many arguments\n"); + return 0; + } - if (!token[0]) - parse_err("no argument"); + if (!token[0]) { + pr_err("no argument\n"); + return 0; + } name = token[0]; - if (strlen(name) + 1 > 80) - parse_err("device name too long"); + if (strlen(name) + 1 > 80) { + pr_err("device name too long\n"); + return 0; + } if (token[1]) { ret = parse_num(&erase_size, token[1]); if (ret) { - parse_err("illegal erase size"); + pr_err("illegal erase size\n"); + return 0; } } @@ -444,8 +447,9 @@ static void block2mtd_exit(void) struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); block2mtd_sync(&dev->mtd); mtd_device_unregister(&dev->mtd); - INFO("mtd%d: [%s] removed", dev->mtd.index, - dev->mtd.name + strlen("block2mtd: ")); + pr_info("mtd%d: [%s] removed\n", + dev->mtd.index, + dev->mtd.name + strlen("block2mtd: ")); list_del(&dev->list); block2mtd_free_device(dev); } -- cgit v0.10.2 From 4c073cd26e0835ae9c807a6188272e09d3087d85 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 17 Apr 2013 13:38:09 -0300 Subject: mtd: nand: pxa3xx: Use devm_kzalloc Replace regular kzalloc with managed devm_kzalloc which simplifies the error path. Signed-off-by: Ezequiel Garcia Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index dec80ca..873eba5 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1035,12 +1035,10 @@ static int alloc_nand_resource(struct platform_device *pdev) int ret, irq, cs; pdata = pdev->dev.platform_data; - info = kzalloc(sizeof(*info) + (sizeof(*mtd) + - sizeof(*host)) * pdata->num_cs, GFP_KERNEL); - if (!info) { - dev_err(&pdev->dev, "failed to allocate memory\n"); + info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) + + sizeof(*host)) * pdata->num_cs, GFP_KERNEL); + if (!info) return -ENOMEM; - } info->pdev = pdev; for (cs = 0; cs < pdata->num_cs; cs++) { @@ -1072,8 +1070,7 @@ static int alloc_nand_resource(struct platform_device *pdev) info->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed to get nand clock\n"); - ret = PTR_ERR(info->clk); - goto fail_free_mtd; + return PTR_ERR(info->clk); } clk_enable(info->clk); @@ -1165,8 +1162,6 @@ fail_free_res: fail_put_clk: clk_disable(info->clk); clk_put(info->clk); -fail_free_mtd: - kfree(info); return ret; } @@ -1202,7 +1197,6 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) for (cs = 0; cs < pdata->num_cs; cs++) nand_release(info->host[cs]->mtd); - kfree(info); return 0; } -- cgit v0.10.2 From 0ddd846f56a7648b9e70fb94a2b93b5b8b6ae335 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 17 Apr 2013 13:38:10 -0300 Subject: mtd: nand: pxa3xx: Use devm_ioremap_resource Using the new devm_ioremap_resource() we can greatly simplify resource handling. Signed-off-by: Ezequiel Garcia Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 873eba5..98b3cea 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1108,30 +1108,16 @@ static int alloc_nand_resource(struct platform_device *pdev) } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no IO memory resource defined\n"); - ret = -ENODEV; + info->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(info->mmio_base)) { + ret = PTR_ERR(info->mmio_base); goto fail_put_clk; } - - r = request_mem_region(r->start, resource_size(r), pdev->name); - if (r == NULL) { - dev_err(&pdev->dev, "failed to request memory resource\n"); - ret = -EBUSY; - goto fail_put_clk; - } - - info->mmio_base = ioremap(r->start, resource_size(r)); - if (info->mmio_base == NULL) { - dev_err(&pdev->dev, "ioremap() failed\n"); - ret = -ENODEV; - goto fail_free_res; - } info->mmio_phys = r->start; ret = pxa3xx_nand_init_buff(info); if (ret) - goto fail_free_io; + goto fail_put_clk; /* initialize all interrupts to be disabled */ disable_int(info, NDSR_MASK); @@ -1155,10 +1141,6 @@ fail_free_buf: info->data_buff, info->data_buff_phys); } else kfree(info->data_buff); -fail_free_io: - iounmap(info->mmio_base); -fail_free_res: - release_mem_region(r->start, resource_size(r)); fail_put_clk: clk_disable(info->clk); clk_put(info->clk); @@ -1169,7 +1151,6 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) { struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); struct pxa3xx_nand_platform_data *pdata; - struct resource *r; int irq, cs; if (!info) @@ -1188,10 +1169,6 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) } else kfree(info->data_buff); - iounmap(info->mmio_base); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); - clk_disable(info->clk); clk_put(info->clk); -- cgit v0.10.2 From 9ca7944deb25d415f7fc5915b9828a857161b97f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 17 Apr 2013 13:38:11 -0300 Subject: mtd: nand: pxa3xx: Use devm_clk_get Replacing clk_get by managed devm_clk_get, the error path can be greatly simplified. Signed-off-by: Ezequiel Garcia Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 98b3cea..1883581 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1067,7 +1067,7 @@ static int alloc_nand_resource(struct platform_device *pdev) spin_lock_init(&chip->controller->lock); init_waitqueue_head(&chip->controller->wq); - info->clk = clk_get(&pdev->dev, NULL); + info->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed to get nand clock\n"); return PTR_ERR(info->clk); @@ -1087,7 +1087,7 @@ static int alloc_nand_resource(struct platform_device *pdev) if (r == NULL) { dev_err(&pdev->dev, "no resource defined for data DMA\n"); ret = -ENXIO; - goto fail_put_clk; + goto fail_disable_clk; } info->drcmr_dat = r->start; @@ -1095,7 +1095,7 @@ static int alloc_nand_resource(struct platform_device *pdev) if (r == NULL) { dev_err(&pdev->dev, "no resource defined for command DMA\n"); ret = -ENXIO; - goto fail_put_clk; + goto fail_disable_clk; } info->drcmr_cmd = r->start; } @@ -1104,20 +1104,20 @@ static int alloc_nand_resource(struct platform_device *pdev) if (irq < 0) { dev_err(&pdev->dev, "no IRQ resource defined\n"); ret = -ENXIO; - goto fail_put_clk; + goto fail_disable_clk; } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); info->mmio_base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(info->mmio_base)) { ret = PTR_ERR(info->mmio_base); - goto fail_put_clk; + goto fail_disable_clk; } info->mmio_phys = r->start; ret = pxa3xx_nand_init_buff(info); if (ret) - goto fail_put_clk; + goto fail_disable_clk; /* initialize all interrupts to be disabled */ disable_int(info, NDSR_MASK); @@ -1141,9 +1141,8 @@ fail_free_buf: info->data_buff, info->data_buff_phys); } else kfree(info->data_buff); -fail_put_clk: +fail_disable_clk: clk_disable(info->clk); - clk_put(info->clk); return ret; } @@ -1170,7 +1169,6 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) kfree(info->data_buff); clk_disable(info->clk); - clk_put(info->clk); for (cs = 0; cs < pdata->num_cs; cs++) nand_release(info->host[cs]->mtd); -- cgit v0.10.2 From fb32061ffaa9da61f72f79120a39729aa1736de0 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 17 Apr 2013 13:38:12 -0300 Subject: mtd: nand: pxa3xx: Use clk_prepare_enable and clk_disable_unprepare This patch converts the module to use clk_prepare_enable and clk_disable_unprepare variants as required by common clock framework. Signed-off-by: Ezequiel Garcia Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 1883581..1c03f9c 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1072,7 +1072,7 @@ static int alloc_nand_resource(struct platform_device *pdev) dev_err(&pdev->dev, "failed to get nand clock\n"); return PTR_ERR(info->clk); } - clk_enable(info->clk); + clk_prepare_enable(info->clk); /* * This is a dirty hack to make this driver work from devicetree @@ -1142,7 +1142,7 @@ fail_free_buf: } else kfree(info->data_buff); fail_disable_clk: - clk_disable(info->clk); + clk_disable_unprepare(info->clk); return ret; } @@ -1168,7 +1168,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) } else kfree(info->data_buff); - clk_disable(info->clk); + clk_disable_unprepare(info->clk); for (cs = 0; cs < pdata->num_cs; cs++) nand_release(info->host[cs]->mtd); -- cgit v0.10.2 From 1f8eaff2aa75f8ec662e044deecdc91abc780d60 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 17 Apr 2013 13:38:13 -0300 Subject: mtd: nand: pxa3xx: Check for clk_prepare_enable() return value clk_prepare_enable() can fail due to unknown reason. Add a check for this and return the error code if it fails. Signed-off-by: Ezequiel Garcia Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 1c03f9c..0f820ca 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1072,7 +1072,9 @@ static int alloc_nand_resource(struct platform_device *pdev) dev_err(&pdev->dev, "failed to get nand clock\n"); return PTR_ERR(info->clk); } - clk_prepare_enable(info->clk); + ret = clk_prepare_enable(info->clk); + if (ret < 0) + return ret; /* * This is a dirty hack to make this driver work from devicetree -- cgit v0.10.2 From 498b6145b8878c3659dfe57921deb7802196ce43 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 17 Apr 2013 13:38:14 -0300 Subject: mtd: nand: pxa3xx: Move buffer release code to its own function Create a function to release the buffer and the dma channel, thus undoing what pxa3xx_nand_init_buff() did. This commit makes the code more readable and will allow to handle non-DMA capable platforms easier. Signed-off-by: Ezequiel Garcia Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 0f820ca..14d3db5 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -912,6 +912,18 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) return 0; } +static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) +{ + struct platform_device *pdev = info->pdev; + if (use_dma) { + pxa_free_dma(info->data_dma_ch); + dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE, + info->data_buff, info->data_buff_phys); + } else { + kfree(info->data_buff); + } +} + static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info) { struct mtd_info *mtd; @@ -1137,12 +1149,7 @@ static int alloc_nand_resource(struct platform_device *pdev) fail_free_buf: free_irq(irq, info); - if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); + pxa3xx_nand_free_buff(info); fail_disable_clk: clk_disable_unprepare(info->clk); return ret; @@ -1163,12 +1170,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq >= 0) free_irq(irq, info); - if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); + pxa3xx_nand_free_buff(info); clk_disable_unprepare(info->clk); -- cgit v0.10.2 From 52778b2e9fcb66c8f1c9d5b1ae435815c19e7ae3 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 15 May 2013 16:40:25 +0800 Subject: mtd: increase max OOB size to 744 The oob size of Micron's MT29F64G08CBABAWP is 744 bytes. So increase the NAND_MAX_OOBSIZE to 744. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 50c5ea0..38535eb4 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -56,7 +56,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); * is supported now. If you add a chip with bigger oobsize/page * adjust this accordingly. */ -#define NAND_MAX_OOBSIZE 640 +#define NAND_MAX_OOBSIZE 744 #define NAND_MAX_PAGESIZE 8192 /* -- cgit v0.10.2 From 64ddba4d8a381b65bebee24c8da4eb80080c64a4 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Mon, 29 Apr 2013 14:07:48 +0200 Subject: mtd: nand: fsmc: update of OF support Add nand bank selection and timings to the device tree bindings. Signed-off-by: Mian Yousaf Kaukab [Added some documentation] Signed-off-by: Linus Walleij Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt index 2240ac0..ec42935 100644 --- a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt +++ b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt @@ -1,4 +1,5 @@ -* FSMC NAND +ST Microelectronics Flexible Static Memory Controller (FSMC) +NAND Interface Required properties: - compatible : "st,spear600-fsmc-nand", "stericsson,fsmc-nand" @@ -9,6 +10,26 @@ Optional properties: - bank-width : Width (in bytes) of the device. If not present, the width defaults to 1 byte - nand-skip-bbtscan: Indicates the the BBT scanning should be skipped +- timings: array of 6 bytes for NAND timings. The meanings of these bytes + are: + byte 0 TCLR : CLE to RE delay in number of AHB clock cycles, only 4 bits + are valid. Zero means one clockcycle, 15 means 16 clock + cycles. + byte 1 TAR : ALE to RE delay, 4 bits are valid. Same format as TCLR. + byte 2 THIZ : number of HCLK clock cycles during which the data bus is + kept in Hi-Z (tristate) after the start of a write access. + Only valid for write transactions. Zero means zero cycles, + 255 means 255 cycles. + byte 3 THOLD : number of HCLK clock cycles to hold the address (and data + when writing) after the command deassertation. Zero means + one cycle, 255 means 256 cycles. + byte 4 TWAIT : number of HCLK clock cycles to assert the command to the + NAND flash in response to SMWAITn. Zero means 1 cycle, + 255 means 256 cycles. + byte 5 TSET : number of HCLK clock cycles to assert the address before the + command is asserted. Zero means one cycle, 255 means 256 + cycles. +- bank: default NAND bank to use (0-3 are valid, 0 is the default). Example: @@ -24,6 +45,8 @@ Example: bank-width = <1>; nand-skip-bbtscan; + timings = /bits/ 8 <0 0 0 2 3 0>; + bank = <1>; partition@0 { ... diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 0e5a1d9..0378386 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -889,6 +889,24 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev, if (of_get_property(np, "nand-skip-bbtscan", NULL)) pdata->options = NAND_SKIP_BBTSCAN; + pdata->nand_timings = devm_kzalloc(&pdev->dev, + sizeof(*pdata->nand_timings), GFP_KERNEL); + if (!pdata->nand_timings) { + dev_err(&pdev->dev, "no memory for nand_timing\n"); + return -ENOMEM; + } + of_property_read_u8_array(np, "timings", (u8 *)pdata->nand_timings, + sizeof(*pdata->nand_timings)); + + /* Set default NAND bank to 0 */ + pdata->bank = 0; + if (!of_property_read_u32(np, "bank", &val)) { + if (val > 3) { + dev_err(&pdev->dev, "invalid bank %u\n", val); + return -EINVAL; + } + pdata->bank = val; + } return 0; } #else diff --git a/include/linux/mtd/fsmc.h b/include/linux/mtd/fsmc.h index d6ed61e..c8be32e 100644 --- a/include/linux/mtd/fsmc.h +++ b/include/linux/mtd/fsmc.h @@ -137,6 +137,7 @@ enum access_mode { /** * fsmc_nand_platform_data - platform specific NAND controller config + * @nand_timings: timing setup for the physical NAND interface * @partitions: partition table for the platform, use a default fallback * if this is NULL * @nr_partitions: the number of partitions in the previous entry -- cgit v0.10.2 From d914c932d7764bf9633af974940aee9390adf20d Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Wed, 29 May 2013 15:30:13 +0300 Subject: mtd: nand_base: Only use GET/SET FEATURES command on chips that support them. Spansion's S34MLx chips support ONFI but not the GET/SET FEATURES calls. Signed-off-by: David Mosberger Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index edc7663..1cbacff 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2709,7 +2709,9 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, { int status; - if (!chip->onfi_version) + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); @@ -2730,7 +2732,9 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - if (!chip->onfi_version) + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; /* clear the sub feature parameters */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 38535eb4..0745a42 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -217,6 +217,9 @@ struct nand_chip; /* ONFI subfeature parameters length */ #define ONFI_SUBFEATURE_PARAM_LEN 4 +/* ONFI optional commands SET/GET FEATURES supported? */ +#define ONFI_OPT_CMD_SET_GET_FEATURES (1 << 2) + struct nand_onfi_params { /* rev info and features block */ /* 'O' 'N' 'F' 'I' */ -- cgit v0.10.2 From 59bff7fb7a0b461320744a9a9b7bf75b8eeddea8 Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Thu, 2 May 2013 15:56:10 +0200 Subject: mtd: nand: davinci: use devm_ioremap_resource() Replace a call to deprecated devm_request_and_ioremap by devm_ioremap_resource. Found with coccicheck and this semantic patch: scripts/coccinelle/api/devm_request_and_ioremap.cocci. Signed-off-by: Laurent Navet Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index c3e15a5..999ad9c 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -623,11 +623,14 @@ static int __init nand_davinci_probe(struct platform_device *pdev) goto err_nomem; } - vaddr = devm_request_and_ioremap(&pdev->dev, res1); - base = devm_request_and_ioremap(&pdev->dev, res2); - if (!vaddr || !base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -EADDRNOTAVAIL; + vaddr = devm_ioremap_resource(&pdev->dev, res1); + if (IS_ERR(vaddr)) { + ret = PTR_ERR(vaddr); + goto err_ioremap; + } + base = devm_ioremap_resource(&pdev->dev, res2); + if (IS_ERR(base)) { + ret = PTR_ERR(base); goto err_ioremap; } -- cgit v0.10.2 From 33777e6676ca60652bd48c7867641fb779106909 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 2 May 2013 14:18:51 -0700 Subject: mtd: tests: don't print error messages when out-of-memory These strings are now unnecessary and discouraged in the kernel. The kernel will have plenty of big scary messages if kmalloc fails. These now only serve to bloat the module. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c index 3e24b37..ab81e9a 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/mtd_oobtest.c @@ -320,10 +320,8 @@ static int scan_for_bad_eraseblocks(void) int i, bad = 0; bbt = kmalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); + if (!bbt) return -ENOMEM; - } pr_info("scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { @@ -380,15 +378,11 @@ static int __init mtd_oobtest_init(void) err = -ENOMEM; readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!readbuf) { - pr_err("error: cannot allocate memory\n"); + if (!readbuf) goto out; - } writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!writebuf) { - pr_err("error: cannot allocate memory\n"); + if (!writebuf) goto out; - } err = scan_for_bad_eraseblocks(); if (err) diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 0c1140b..acd991f 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -191,10 +191,8 @@ static int crosstest(void) pr_info("crosstest\n"); pp1 = kmalloc(pgsize * 4, GFP_KERNEL); - if (!pp1) { - pr_err("error: cannot allocate memory\n"); + if (!pp1) return -ENOMEM; - } pp2 = pp1 + pgsize; pp3 = pp2 + pgsize; pp4 = pp3 + pgsize; @@ -456,10 +454,8 @@ static int scan_for_bad_eraseblocks(void) int i, bad = 0; bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); + if (!bbt) return -ENOMEM; - } pr_info("scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { @@ -516,20 +512,14 @@ static int __init mtd_pagetest_init(void) err = -ENOMEM; bufsize = pgsize * 2; writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!writebuf) { - pr_err("error: cannot allocate memory\n"); + if (!writebuf) goto out; - } twopages = kmalloc(bufsize, GFP_KERNEL); - if (!twopages) { - pr_err("error: cannot allocate memory\n"); + if (!twopages) goto out; - } boundary = kmalloc(bufsize, GFP_KERNEL); - if (!boundary) { - pr_err("error: cannot allocate memory\n"); + if (!boundary) goto out; - } err = scan_for_bad_eraseblocks(); if (err) diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c index 266de04..2cdd0c4 100644 --- a/drivers/mtd/tests/mtd_readtest.c +++ b/drivers/mtd/tests/mtd_readtest.c @@ -143,10 +143,8 @@ static int scan_for_bad_eraseblocks(void) int i, bad = 0; bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); + if (!bbt) return -ENOMEM; - } if (!mtd_can_have_bb(mtd)) return 0; @@ -204,15 +202,11 @@ static int __init mtd_readtest_init(void) err = -ENOMEM; iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!iobuf) { - pr_err("error: cannot allocate memory\n"); + if (!iobuf) goto out; - } iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!iobuf1) { - pr_err("error: cannot allocate memory\n"); + if (!iobuf1) goto out; - } err = scan_for_bad_eraseblocks(); if (err) diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c index a6ce9c1..20b63d1 100644 --- a/drivers/mtd/tests/mtd_speedtest.c +++ b/drivers/mtd/tests/mtd_speedtest.c @@ -313,10 +313,8 @@ static int scan_for_bad_eraseblocks(void) int i, bad = 0; bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); + if (!bbt) return -ENOMEM; - } if (!mtd_can_have_bb(mtd)) goto out; @@ -384,10 +382,8 @@ static int __init mtd_speedtest_init(void) err = -ENOMEM; iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!iobuf) { - pr_err("error: cannot allocate memory\n"); + if (!iobuf) goto out; - } prandom_bytes(iobuf, mtd->erasesize); diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c index 787f539..3a95e61 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/mtd_stresstest.c @@ -202,10 +202,8 @@ static int scan_for_bad_eraseblocks(void) int i, bad = 0; bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); + if (!bbt) return -ENOMEM; - } if (!mtd_can_have_bb(mtd)) return 0; @@ -276,10 +274,8 @@ static int __init mtd_stresstest_init(void) readbuf = vmalloc(bufsize); writebuf = vmalloc(bufsize); offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL); - if (!readbuf || !writebuf || !offsets) { - pr_err("error: cannot allocate memory\n"); + if (!readbuf || !writebuf || !offsets) goto out; - } for (i = 0; i < ebcnt; i++) offsets[i] = mtd->erasesize; prandom_bytes(writebuf, bufsize); diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c index aade56f..e41a04f 100644 --- a/drivers/mtd/tests/mtd_subpagetest.c +++ b/drivers/mtd/tests/mtd_subpagetest.c @@ -333,10 +333,8 @@ static int scan_for_bad_eraseblocks(void) int i, bad = 0; bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); + if (!bbt) return -ENOMEM; - } pr_info("scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { @@ -393,15 +391,11 @@ static int __init mtd_subpagetest_init(void) err = -ENOMEM; bufsize = subpgsize * 32; writebuf = kmalloc(bufsize, GFP_KERNEL); - if (!writebuf) { - pr_info("error: cannot allocate memory\n"); + if (!writebuf) goto out; - } readbuf = kmalloc(bufsize, GFP_KERNEL); - if (!readbuf) { - pr_info("error: cannot allocate memory\n"); + if (!readbuf) goto out; - } err = scan_for_bad_eraseblocks(); if (err) -- cgit v0.10.2 From 7f1e0419f9a8737327d7ccb61ae40f9cc22b8e49 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 17:07:41 +0530 Subject: mtd: onenand/generic: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Kyungmin Park Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 9f11562..42520a3 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -94,8 +94,6 @@ static int generic_onenand_remove(struct platform_device *pdev) struct resource *res = pdev->resource; unsigned long size = resource_size(res); - platform_set_drvdata(pdev, NULL); - if (info) { onenand_release(&info->mtd); release_mem_region(res->start, size); -- cgit v0.10.2 From f6ac1a410af63193542210024c3950fd1ea8ffb5 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 17:07:42 +0530 Subject: mtd: onenand/omap2: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Jarkko Lavinen Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index d98b198..cd43cea 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -810,7 +810,6 @@ static int omap2_onenand_remove(struct platform_device *pdev) if (c->dma_channel != -1) omap_free_dma(c->dma_channel); omap2_onenand_shutdown(pdev); - platform_set_drvdata(pdev, NULL); if (c->gpio_irq) { free_irq(gpio_to_irq(c->gpio_irq), c); gpio_free(c->gpio_irq); -- cgit v0.10.2 From cf3b55a994a04c31ce3ac3119880582547e22cf2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 17:07:43 +0530 Subject: mtd: onenand/samsung: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Kyungmin Park Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index 2cf7408..cebc48d 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -1073,7 +1073,6 @@ static int s3c_onenand_remove(struct platform_device *pdev) release_mem_region(onenand->base_res->start, resource_size(onenand->base_res)); - platform_set_drvdata(pdev, NULL); kfree(onenand->oob_buf); kfree(onenand->page_buf); kfree(onenand); -- cgit v0.10.2 From d1cb556c3feb238f11dc47f8fa56df7880022908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sat, 4 May 2013 15:19:35 +0200 Subject: mtd: gpmi-nand: fix error return from gpmi_get_clks() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Mirosław Acked-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 25ecfa1..1d18efc 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -473,12 +473,14 @@ static int gpmi_get_clks(struct gpmi_nand_data *this) struct resources *r = &this->resources; char **extra_clks = NULL; struct clk *clk; - int i; + int err, i; /* The main clock is stored in the first. */ r->clock[0] = clk_get(this->dev, "gpmi_io"); - if (IS_ERR(r->clock[0])) + if (IS_ERR(r->clock[0])) { + err = PTR_ERR(r->clock[0]); goto err_clock; + } /* Get extra clocks */ if (GPMI_IS_MX6Q(this)) @@ -491,8 +493,10 @@ static int gpmi_get_clks(struct gpmi_nand_data *this) break; clk = clk_get(this->dev, extra_clks[i - 1]); - if (IS_ERR(clk)) + if (IS_ERR(clk)) { + err = PTR_ERR(clk); goto err_clock; + } r->clock[i] = clk; } @@ -511,7 +515,7 @@ static int gpmi_get_clks(struct gpmi_nand_data *this) err_clock: dev_dbg(this->dev, "failed in finding the clocks.\n"); gpmi_put_clks(this); - return -ENOMEM; + return err; } static int acquire_resources(struct gpmi_nand_data *this) -- cgit v0.10.2 From 71b94e2e866aa35f40945d9e820fc3214b792d1f Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Thu, 9 May 2013 15:34:54 +0800 Subject: mtd: atmel_nand: replace cpu_is_at32ap7000() with a nand platform data The nand driver use cpu_is_at32ap7000() macro for a workaround. For the multi-platform support, we will remove this cpu_is_xxx() macro. This patch adds a boolean variable need_reset_workaround in structure atmel_nand_data. Using this variable we can remove cpu_is_at32ap7000() macro. Hans-Christian: Feel free to push this through the mtd tree, if they won't accept it I'm working on getting my workflow up on the linux-avr32.git tree. Signed-off-by: Josh Wu Acked-by: Hans-Christian Egtvedt Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 7f8759a..a68f3cf 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1983,6 +1983,9 @@ at32_add_device_nand(unsigned int id, struct atmel_nand_data *data) ARRAY_SIZE(smc_cs3_resource))) goto fail; + /* For at32ap7000, we use the reset workaround for nand driver */ + data->need_reset_workaround = true; + if (platform_device_add_data(pdev, data, sizeof(struct atmel_nand_data))) goto fail; diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 2d23d29..7bf912b 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1174,10 +1174,9 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, * Workaround: Reset the parity registers before reading the * actual data. */ - if (cpu_is_at32ap7000()) { - struct atmel_nand_host *host = chip->priv; + struct atmel_nand_host *host = chip->priv; + if (host->board.need_reset_workaround) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); - } /* read the page */ chip->read_buf(mtd, p, eccsize); @@ -1298,11 +1297,11 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, */ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) { - if (cpu_is_at32ap7000()) { - struct nand_chip *nand_chip = mtd->priv; - struct atmel_nand_host *host = nand_chip->priv; + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + + if (host->board.need_reset_workaround) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); - } } #if defined(CONFIG_OF) diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index 6a293b7..59f558d 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -71,6 +71,9 @@ struct atmel_nand_data { u8 on_flash_bbt; /* bbt on flash */ struct mtd_partition *parts; unsigned int num_parts; + + /* default is false, only for at32ap7000 chip is true */ + bool need_reset_workaround; }; /* Serial */ -- cgit v0.10.2 From 1b7192658a08f70df0f290634fd7cd2ecb629fc9 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Thu, 9 May 2013 15:34:55 +0800 Subject: mtd: atmel_nand: add a new dt binding item for nand dma support This patch will set the nand dma support in dts. Since we will not use cpu_is_xxx() in nand driver. We needn't include the mach/cpu.h any more. Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt index d555421..b6eb484 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -15,6 +15,7 @@ Required properties: optional gpio and may be set to 0 if not present. Optional properties: +- atmel,nand-has-dma : boolean to support dma transfer for nand read/write. - nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default. Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", "soft_bch". diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 7bf912b..61d3869 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -43,8 +43,6 @@ #include #include -#include - static int use_dma = 1; module_param(use_dma, int, 0); @@ -128,11 +126,6 @@ struct atmel_nand_host { static struct nand_ecclayout atmel_pmecc_oobinfo; -static int cpu_has_dma(void) -{ - return cpu_is_at91sam9rl() || cpu_is_at91sam9g45(); -} - /* * Enable NAND. */ @@ -1336,6 +1329,8 @@ static int atmel_of_init_port(struct atmel_nand_host *host, board->on_flash_bbt = of_get_nand_on_flash_bbt(np); + board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma"); + if (of_get_nand_bus_width(np) == 16) board->bus_width_16 = 1; @@ -1600,7 +1595,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) nand_chip->bbt_options |= NAND_BBT_USE_FLASH; } - if (!cpu_has_dma()) + if (!host->board.has_dma) use_dma = 0; if (use_dma) { diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index 59f558d..cea9f70 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -71,6 +71,7 @@ struct atmel_nand_data { u8 on_flash_bbt; /* bbt on flash */ struct mtd_partition *parts; unsigned int num_parts; + bool has_dma; /* support dma transfer */ /* default is false, only for at32ap7000 chip is true */ bool need_reset_workaround; -- cgit v0.10.2 From 0a60d049812e959b43b0556ef5b914d0900b6283 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 14 May 2013 08:15:21 -0300 Subject: mtd: nand: pxa3xx: Set info->use_dma properly Currently, the variable info->use_dma is never set and always zero-valued which means the driver never does DMA transfers. We fix this by simply setting info->use_dma to the module parameter, also named 'use_dma'. Note that the module parameter has the same name, but different semantics. This fixes a regression introduced by the below commit which removed the info->use_dma variable set. commit 4eb2da8994042d68e84e31138788429a102da2ea Author: Lei Wen Date: Mon Feb 28 10:32:13 2011 +0800 mtd: pxa3xx_nand: unify prepare command Before the above commit, the driver had use_dma=1 on all NAND commands except on CMD_STATUS. This behavior is long lost and we are not recovering in this patch, either. This was spotted and verified by human inspection. Signed-off-by: Ezequiel Garcia Tested-by: Nikita Kiryanov Acked-by: Igor Grinberg Reviewed-by: Haojian Zhuang Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 14d3db5..cef1eeb 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -506,6 +506,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, info->buf_count = 0; info->oob_size = 0; info->use_ecc = 0; + info->use_dma = (use_dma) ? 1 : 0; info->is_ready = 0; info->retcode = ERR_NONE; if (info->cs != 0) -- cgit v0.10.2 From a33e435c2822d9c6351deed1cda0184638cfbb42 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 14 May 2013 08:15:22 -0300 Subject: mtd: nand: pxa3xx: Use of_machine_is_compatible() This patch replaces cpu_is_pxa3xx() with of_machine_is_compatible() which allows to build this driver for other platforms than ARCH_PXA. Signed-off-by: Ezequiel Garcia Tested-by: Nikita Kiryanov Acked-by: Igor Grinberg Reviewed-by: Haojian Zhuang Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index cef1eeb..8386e9a 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1094,7 +1094,7 @@ static int alloc_nand_resource(struct platform_device *pdev) * bindings. It can be removed once we have a prober DMA controller * framework for DT. */ - if (pdev->dev.of_node && cpu_is_pxa3xx()) { + if (pdev->dev.of_node && of_machine_is_compatible("marvell,pxa3xx")) { info->drcmr_dat = 97; info->drcmr_cmd = 99; } else { -- cgit v0.10.2 From f3958984259f01503ff9629f3564b11bf57a98be Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 14 May 2013 08:15:23 -0300 Subject: mtd: nand: pxa3xx: Fix MODULE_DEVICE_TABLE declaration This module's device table is incorrectly declared using i2c_pxa_dt_ids, instead of pxa3xx_nand_dt_ids. Signed-off-by: Ezequiel Garcia Tested-by: Nikita Kiryanov Acked-by: Igor Grinberg Reviewed-by: Haojian Zhuang Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 8386e9a..e4e697f 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1185,7 +1185,7 @@ static struct of_device_id pxa3xx_nand_dt_ids[] = { { .compatible = "marvell,pxa3xx-nand" }, {} }; -MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids); +MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); static int pxa3xx_nand_probe_dt(struct platform_device *pdev) { -- cgit v0.10.2 From d14231f1985f31441777a2dbda055dc8499f6c93 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 14 May 2013 08:15:24 -0300 Subject: mtd: nand: pxa3xx: Add address support for READID command This allows to support READID ONFI command which sends 0x20 as address together with the 0x90 READID command. This is required to detect ONFI compliant devices. Signed-off-by: Ezequiel Garcia Tested-by: Nikita Kiryanov Acked-by: Igor Grinberg Reviewed-by: Haojian Zhuang Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index e4e697f..36780e2 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -596,6 +596,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, info->ndcb0 |= NDCB0_CMD_TYPE(3) | NDCB0_ADDR_CYC(1) | cmd; + info->ndcb1 = (column & 0xFF); info->data_size = 8; break; -- cgit v0.10.2 From ce0268f650ad26e103d82ce5a58379737adc2ab9 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 14 May 2013 08:15:25 -0300 Subject: mtd: nand: pxa3xx: Add support for Read parameter page command This command is required to identify ONFI-compliant devices. Signed-off-by: Ezequiel Garcia Tested-by: Nikita Kiryanov Acked-by: Igor Grinberg Reviewed-by: Haojian Zhuang Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 36780e2..d63d378 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -590,6 +590,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, | addr_cycle; break; + case NAND_CMD_PARAM: + cmd = NAND_CMD_PARAM; + info->buf_count = 256; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | NDCB0_ADDR_CYC(1) + | cmd; + info->ndcb1 = (column & 0xFF); + info->data_size = 256; + break; + case NAND_CMD_READID: cmd = host->cmdset->read_id; info->buf_count = host->read_id_bytes; -- cgit v0.10.2 From 8f1f06272000fe970d211060bd7a73956eadd0c4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:32:47 +0900 Subject: mtd: nand: ams-delta: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index f1d71cd..8611eb4 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -258,7 +258,6 @@ static int ams_delta_init(struct platform_device *pdev) 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_free: -- cgit v0.10.2 From 91e859211af012555672e090106183a691ed71e2 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:33:27 +0900 Subject: mtd: nand: atmel_nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Josh Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 61d3869..7e50ed8 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1659,7 +1659,6 @@ err_hw_ecc: err_scan_ident: err_no_card: atmel_nand_disable(host); - platform_set_drvdata(pdev, NULL); if (host->dma_chan) dma_release_channel(host->dma_chan); err_ecc_ioremap: -- cgit v0.10.2 From 2ab8e886397b52fe46e03ce646b3fa9ef3ac4cc5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:34:07 +0900 Subject: mtd: nand: bf5xx_nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 776df36..809a63c 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -671,8 +671,6 @@ static int bf5xx_nand_remove(struct platform_device *pdev) { struct bf5xx_nand_info *info = to_nand_info(pdev); - platform_set_drvdata(pdev, NULL); - /* first thing we need to do is release all our mtds * and their partitions, then go through freeing the * resources used @@ -832,7 +830,6 @@ static int bf5xx_nand_probe(struct platform_device *pdev) out_err_nand_scan: bf5xx_nand_dma_remove(info); out_err_hw_init: - platform_set_drvdata(pdev, NULL); kfree(info); out_err_kzalloc: peripheral_free_list(bfin_nfc_pin_req); -- cgit v0.10.2 From 4fdcd3ff963bb53b141f8c3624cc5e54cacc8937 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:34:48 +0900 Subject: mtd: nand: docg4: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Mike Dunn Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index fa25e7a..dc86d4a 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -1368,7 +1368,6 @@ static int __init probe_docg4(struct platform_device *pdev) 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); } @@ -1380,7 +1379,6 @@ 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); -- cgit v0.10.2 From ccc109d5b85a3305417a0b22deb65705737af34a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:36:18 +0900 Subject: mtd: nand: fsmc_nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 0378386..2a3b1b9 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -1192,8 +1192,6 @@ static int fsmc_nand_remove(struct platform_device *pdev) { struct fsmc_nand_data *host = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (host) { nand_release(&host->mtd); -- cgit v0.10.2 From ed3b49b91655bdbed7d93011eb9e4dd05ffd63d4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:37:16 +0900 Subject: mtd: nand: gpmi-nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Shawn Guo Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 1d18efc..b741a6c 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1637,7 +1637,6 @@ static int gpmi_nand_probe(struct platform_device *pdev) exit_nfc_init: release_resources(this); exit_acquire_resources: - platform_set_drvdata(pdev, NULL); dev_err(this->dev, "driver registration failed: %d\n", ret); kfree(this); @@ -1650,7 +1649,6 @@ static int gpmi_nand_remove(struct platform_device *pdev) gpmi_nfc_exit(this); release_resources(this); - platform_set_drvdata(pdev, NULL); kfree(this); return 0; } -- cgit v0.10.2 From 1b03147277e38ef0cbe0f92bc2b908c7419b55ea Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:38:25 +0900 Subject: mtd: nand: jz4740_nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index b76460e..698a4d1 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -538,7 +538,6 @@ err_unclaim_banks: err_gpio_busy: if (pdata && gpio_is_valid(pdata->busy_gpio)) gpio_free(pdata->busy_gpio); - platform_set_drvdata(pdev, NULL); err_iounmap_mmio: jz_nand_iounmap_resource(nand->mem, nand->base); err_free: @@ -570,7 +569,6 @@ static int jz_nand_remove(struct platform_device *pdev) jz_nand_iounmap_resource(nand->mem, nand->base); - platform_set_drvdata(pdev, NULL); kfree(nand); return 0; -- cgit v0.10.2 From 6f911f78e631a9bb03f6be4372aa01b36c2460ee Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:39:15 +0900 Subject: mtd: nand: lpc32xx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Tested-by: Roland Stigge Acked-by: Roland Stigge Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index fd1df5e..74a469b 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -828,7 +828,6 @@ err_exit3: err_exit2: clk_disable(host->clk); clk_put(host->clk); - platform_set_drvdata(pdev, NULL); err_exit1: lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); @@ -851,7 +850,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev) clk_disable(host->clk); clk_put(host->clk); - platform_set_drvdata(pdev, NULL); lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index be94ed5..a66f33f 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -936,7 +936,6 @@ err_exit3: err_exit2: clk_disable(host->clk); clk_put(host->clk); - platform_set_drvdata(pdev, NULL); err_exit1: lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); @@ -963,7 +962,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev) clk_disable(host->clk); clk_put(host->clk); - platform_set_drvdata(pdev, NULL); lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); -- cgit v0.10.2 From 62416c0724dd66fbfc7802cf0a9269682233aa86 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:40:07 +0900 Subject: mtd: nand: mxc_nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 07e5784..18855ad 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1578,8 +1578,6 @@ static int mxcnd_remove(struct platform_device *pdev) { struct mxc_nand_host *host = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - nand_release(&host->mtd); return 0; -- cgit v0.10.2 From 399b6b7df01b908fa34e59a6c1307563014c5d27 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:40:47 +0900 Subject: mtd: nand: nuc900_nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index cd6be2e..5211515 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -324,8 +324,6 @@ static int nuc900_nand_remove(struct platform_device *pdev) kfree(nuc900_nand); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v0.10.2 From 9fa34a1eb6116b051a4f501d238d0e683e255f7d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:17:36 +0900 Subject: mtd: devices: spear_smi: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Viresh Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 0e1c22e..092bb68 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -995,14 +995,12 @@ static int spear_smi_probe(struct platform_device *pdev) ret = spear_smi_setup_banks(pdev, i, pdata->np[i]); if (ret) { dev_err(&dev->pdev->dev, "bank setup failed\n"); - goto err_bank_setup; + goto err_irq; } } return 0; -err_bank_setup: - platform_set_drvdata(pdev, NULL); err_irq: clk_disable_unprepare(dev->clk); err: @@ -1040,7 +1038,6 @@ static int spear_smi_remove(struct platform_device *pdev) } clk_disable_unprepare(dev->clk); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v0.10.2 From c15640081b0b89216600c87930a26ca93bc83839 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:41:39 +0900 Subject: mtd: nand: omap2: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 81b80af..daa3dfc 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -2087,7 +2087,6 @@ static int omap_nand_remove(struct platform_device *pdev) mtd); omap3_free_bch(&info->mtd); - platform_set_drvdata(pdev, NULL); if (info->dma) dma_release_channel(info->dma); -- cgit v0.10.2 From 94ba696c12d9a9ea34876fe5fe2bae1463a9c76c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:42:29 +0900 Subject: mtd: nand: orion_nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 8fbd002..46f308d 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -186,7 +186,6 @@ no_dev: clk_disable_unprepare(clk); clk_put(clk); } - platform_set_drvdata(pdev, NULL); iounmap(io_base); no_res: kfree(nc); -- cgit v0.10.2 From c2e89f82251f3674de7e3668c90ef9eea2fba059 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:43:34 +0900 Subject: mtd: nand: plat_nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index c004566..d42700a 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -122,7 +122,6 @@ static int plat_nand_probe(struct platform_device *pdev) out: if (pdata->ctrl.remove) pdata->ctrl.remove(pdev); - platform_set_drvdata(pdev, NULL); iounmap(data->io_base); out_release_io: release_mem_region(res->start, resource_size(res)); -- cgit v0.10.2 From 8461a99095d7f48afee9fb1da9a638c536626b4f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:44:14 +0900 Subject: mtd: nand: pxa3xx_nand: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index d63d378..fdc045d 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1177,7 +1177,6 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) return 0; pdata = pdev->dev.platform_data; - platform_set_drvdata(pdev, NULL); irq = platform_get_irq(pdev, 0); if (irq >= 0) -- cgit v0.10.2 From 9e7c3df8dccef854189673309dc58c4aa2bf1642 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:44:53 +0900 Subject: mtd: nand: s3c2410: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d65afd2..191823e 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -697,8 +697,6 @@ static int s3c24xx_nand_remove(struct platform_device *pdev) { struct s3c2410_nand_info *info = to_nand_info(pdev); - platform_set_drvdata(pdev, NULL); - if (info == NULL) return 0; -- cgit v0.10.2 From b4689a6998ca2e16dac89a56574e39be4750d221 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:45:32 +0900 Subject: mtd: nand: sharpsl: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 127bc42..1d41578 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -194,7 +194,6 @@ err_add: nand_release(&sharpsl->mtd); err_scan: - platform_set_drvdata(pdev, NULL); iounmap(sharpsl->io); err_ioremap: err_get_res: @@ -212,8 +211,6 @@ static int sharpsl_nand_remove(struct platform_device *pdev) /* Release resources, unregister device */ nand_release(&sharpsl->mtd); - platform_set_drvdata(pdev, NULL); - iounmap(sharpsl->io); /* Free the MTD device structure */ -- cgit v0.10.2 From 21ded414b5062eb77643a66117f827fa682e9053 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:46:11 +0900 Subject: mtd: nand: txx9ndfmc: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 7ed654c..0488dd5 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -387,7 +387,6 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int i; - platform_set_drvdata(dev, NULL); if (!drvdata) return 0; for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { -- cgit v0.10.2 From d5d093121e305e7df630a80bd656b357ba6674c1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:18:51 +0900 Subject: mtd: devices: elm: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index fc8ad9f..4fab47a 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -381,7 +381,6 @@ static int elm_remove(struct platform_device *pdev) { pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v0.10.2 From b36b5e9d128b7795794ce84a4ce308ea5c1a3c38 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:19:51 +0900 Subject: mtd: maps: plat-ram: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 71fdda2..b0b85eb 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -84,8 +84,6 @@ static int platram_remove(struct platform_device *pdev) { struct platram_info *info = to_platram_info(pdev); - platform_set_drvdata(pdev, NULL); - dev_dbg(&pdev->dev, "removing device\n"); if (info == NULL) -- cgit v0.10.2 From 8e041c8abd51371b0f588e0f91b9876fa01a6b98 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:20:58 +0900 Subject: mtd: maps: sa1100-flash: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 29e3dca..9e70f8e 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -279,7 +279,6 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev) struct sa_info *info = platform_get_drvdata(pdev); struct flash_platform_data *plat = pdev->dev.platform_data; - platform_set_drvdata(pdev, NULL); sa1100_destroy(info, plat); return 0; -- cgit v0.10.2 From f40152f7b8f9bf4869e659a62e771cfe5d0ca925 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:21:51 +0900 Subject: mtd: maps: latch-addr-flash: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c index ab0fead..675ccb8 100644 --- a/drivers/mtd/maps/latch-addr-flash.c +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -102,7 +102,6 @@ static int latch_addr_flash_remove(struct platform_device *dev) info = platform_get_drvdata(dev); if (info == NULL) return 0; - platform_set_drvdata(dev, NULL); latch_addr_data = dev->dev.platform_data; -- cgit v0.10.2 From 576e7adf822f4cfe6873572a2507108498f1d9ca Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:30:15 +0900 Subject: mtd: maps: pxa2xx-flash: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index acb1dbc..22c55cb 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -107,8 +107,6 @@ static int pxa2xx_flash_remove(struct platform_device *dev) { struct pxa2xx_flash_info *info = platform_get_drvdata(dev); - platform_set_drvdata(dev, NULL); - mtd_device_unregister(info->mtd); map_destroy(info->mtd); -- cgit v0.10.2 From 2d7fde1ede6ce0de15371184839455893010d88d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:30:44 +0900 Subject: mtd: maps: physmap: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index e7a592c..9eb7ead 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -40,7 +40,6 @@ static int physmap_flash_remove(struct platform_device *dev) info = platform_get_drvdata(dev); if (info == NULL) return 0; - platform_set_drvdata(dev, NULL); physmap_data = dev->dev.platform_data; -- cgit v0.10.2 From 55cf4b24ff4e7bc4038201dcb472e55c310a4ca5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:31:29 +0900 Subject: mtd: maps: ixp4xx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Marc Kleine-Budde Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 52b3410..09c542b 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -155,8 +155,6 @@ static int ixp4xx_flash_remove(struct platform_device *dev) struct flash_platform_data *plat = dev->dev.platform_data; struct ixp4xx_flash_info *info = platform_get_drvdata(dev); - platform_set_drvdata(dev, NULL); - if(!info) return 0; -- cgit v0.10.2 From ba2694da2cbb9a86e9e875b4d1f952b8e745d060 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 15:32:03 +0900 Subject: mtd: maps: rbtx4939-flash: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index ac02fbf..ab4ec55 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c @@ -34,7 +34,6 @@ static int rbtx4939_flash_remove(struct platform_device *dev) info = platform_get_drvdata(dev); if (!info) return 0; - platform_set_drvdata(dev, NULL); if (info->mtd) { struct rbtx4939_flash_data *pdata = dev->dev.platform_data; -- cgit v0.10.2 From 11f164d11187331985f8550df9738a588792adf9 Mon Sep 17 00:00:00 2001 From: avinash philip Date: Tue, 18 Jun 2013 00:16:39 +0530 Subject: mtd: devices: elm: Low power transition support ELM is used for locating bit-flip errors in when using BCH ECC scheme. This patch adds suspend/resume support for leaf level ELM driver, And also provides ELM register context save & restore support, so that configurations are preserved across hardware power-off/on transitions. Signed-off-by: Philip Avinash Signed-off-by: Pekon Gupta Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index 4fab47a..4f683d2 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -20,14 +20,21 @@ #include #include #include +#include #include #include +#define ELM_SYSCONFIG 0x010 #define ELM_IRQSTATUS 0x018 #define ELM_IRQENABLE 0x01c #define ELM_LOCATION_CONFIG 0x020 #define ELM_PAGE_CTRL 0x080 #define ELM_SYNDROME_FRAGMENT_0 0x400 +#define ELM_SYNDROME_FRAGMENT_1 0x404 +#define ELM_SYNDROME_FRAGMENT_2 0x408 +#define ELM_SYNDROME_FRAGMENT_3 0x40c +#define ELM_SYNDROME_FRAGMENT_4 0x410 +#define ELM_SYNDROME_FRAGMENT_5 0x414 #define ELM_SYNDROME_FRAGMENT_6 0x418 #define ELM_LOCATION_STATUS 0x800 #define ELM_ERROR_LOCATION_0 0x880 @@ -56,12 +63,27 @@ #define SYNDROME_FRAGMENT_REG_SIZE 0x40 #define ERROR_LOCATION_SIZE 0x100 +struct elm_registers { + u32 elm_irqenable; + u32 elm_sysconfig; + u32 elm_location_config; + u32 elm_page_ctrl; + u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX]; +}; + struct elm_info { struct device *dev; void __iomem *elm_base; struct completion elm_completion; struct list_head list; enum bch_ecc bch_type; + struct elm_registers elm_regs; }; static LIST_HEAD(elm_devices); @@ -384,6 +406,100 @@ static int elm_remove(struct platform_device *pdev) return 0; } +/** + * elm_context_save + * saves ELM configurations to preserve them across Hardware powered-down + */ +static int elm_context_save(struct elm_info *info) +{ + struct elm_registers *regs = &info->elm_regs; + enum bch_ecc bch_type = info->bch_type; + u32 offset = 0, i; + + regs->elm_irqenable = elm_read_reg(info, ELM_IRQENABLE); + regs->elm_sysconfig = elm_read_reg(info, ELM_SYSCONFIG); + regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG); + regs->elm_page_ctrl = elm_read_reg(info, ELM_PAGE_CTRL); + for (i = 0; i < ERROR_VECTOR_MAX; i++) { + offset = i * SYNDROME_FRAGMENT_REG_SIZE; + switch (bch_type) { + case BCH8_ECC: + regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_3 + offset); + regs->elm_syndrome_fragment_2[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_2 + offset); + case BCH4_ECC: + regs->elm_syndrome_fragment_1[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_1 + offset); + regs->elm_syndrome_fragment_0[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_0 + offset); + default: + return -EINVAL; + } + /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs + * to be saved for all BCH schemes*/ + regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_6 + offset); + } + return 0; +} + +/** + * elm_context_restore + * writes configurations saved duing power-down back into ELM registers + */ +static int elm_context_restore(struct elm_info *info) +{ + struct elm_registers *regs = &info->elm_regs; + enum bch_ecc bch_type = info->bch_type; + u32 offset = 0, i; + + elm_write_reg(info, ELM_IRQENABLE, regs->elm_irqenable); + elm_write_reg(info, ELM_SYSCONFIG, regs->elm_sysconfig); + elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config); + elm_write_reg(info, ELM_PAGE_CTRL, regs->elm_page_ctrl); + for (i = 0; i < ERROR_VECTOR_MAX; i++) { + offset = i * SYNDROME_FRAGMENT_REG_SIZE; + switch (bch_type) { + case BCH8_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, + regs->elm_syndrome_fragment_3[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset, + regs->elm_syndrome_fragment_2[i]); + case BCH4_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset, + regs->elm_syndrome_fragment_1[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset, + regs->elm_syndrome_fragment_0[i]); + default: + return -EINVAL; + } + /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */ + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, + regs->elm_syndrome_fragment_6[i] & + ELM_SYNDROME_VALID); + } + return 0; +} + +static int elm_suspend(struct device *dev) +{ + struct elm_info *info = dev_get_drvdata(dev); + elm_context_save(info); + pm_runtime_put_sync(dev); + return 0; +} + +static int elm_resume(struct device *dev) +{ + struct elm_info *info = dev_get_drvdata(dev); + pm_runtime_get_sync(dev); + elm_context_restore(info); + return 0; +} + +static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); + #ifdef CONFIG_OF static const struct of_device_id elm_of_match[] = { { .compatible = "ti,am3352-elm" }, @@ -397,6 +513,7 @@ static struct platform_driver elm_driver = { .name = "elm", .owner = THIS_MODULE, .of_match_table = of_match_ptr(elm_of_match), + .pm = &elm_pm_ops, }, .probe = elm_probe, .remove = elm_remove, -- cgit v0.10.2 From f666d6495a4350ebe4ebb8c071b290f9f49514bf Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Tue, 30 Jul 2013 17:17:29 +0200 Subject: mtd: atmel_nand: using a stronger ECC is not dangerous We don't have to issue a warning when a stronger error correcting capability is chosen. Signed-off-by: Richard Genoud Acked-by: Josh Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 7e50ed8..0871510 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -995,7 +995,7 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, return err_no; } - if (cap != host->pmecc_corr_cap || + if (cap > host->pmecc_corr_cap || sector_size != host->pmecc_sector_size) dev_info(host->dev, "WARNING: Be Caution! Using different PMECC parameters from Nand ONFI ECC reqirement.\n"); -- cgit v0.10.2 From 795add9a56ef084061a4c5dce50a324f52305f08 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 13 May 2013 16:40:39 +0200 Subject: mtd: remove obsolete JEDEC mapping drivers JEDEC device support was removed in v2.6.22. (It had been marked as BROKEN (indirectly) since at least v2.6.12.) When it was removed the two JEDEC mapping drivers that depended on it should have been removed too. Do so now. Signed-off-by: Paul Bolle Acked-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 8b27ca0..310dc7c 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -157,24 +157,6 @@ config MTD_PXA2XX help This provides a driver for the NOR flash attached to a PXA2xx chip. -config MTD_OCTAGON - tristate "JEDEC Flash device mapped on Octagon 5066 SBC" - depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS - help - This provides a 'mapping' driver which supports the way in which - the flash chips are connected in the Octagon-5066 Single Board - Computer. More information on the board is available at - . - -config MTD_VMAX - tristate "JEDEC Flash device mapped on Tempustech VMAX SBC301" - depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS - help - This provides a 'mapping' driver which supports the way in which - the flash chips are connected in the Tempustech VMAX SBC301 Single - Board Computer. More information on the board is available at - . - config MTD_SCx200_DOCFLASH tristate "Flash device mapped with DOCCS on NatSemi SCx200" depends on SCx200 && MTD_CFI diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 9fdbd4b..141c91a 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o -obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o obj-$(CONFIG_MTD_PISMO) += pismo.o @@ -28,7 +27,6 @@ obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o obj-$(CONFIG_MTD_TS5500) += ts5500_flash.o obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o -obj-$(CONFIG_MTD_VMAX) += vmax301.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o diff --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c deleted file mode 100644 index 807ac2a..0000000 --- a/drivers/mtd/maps/octagon-5066.c +++ /dev/null @@ -1,246 +0,0 @@ -/* ###################################################################### - - Octagon 5066 MTD Driver. - - The Octagon 5066 is a SBC based on AMD's 586-WB running at 133 MHZ. It - comes with a builtin AMD 29F016 flash chip and a socketed EEPROM that - is replacable by flash. Both units are mapped through a multiplexer - into a 32k memory window at 0xe8000. The control register for the - multiplexing unit is located at IO 0x208 with a bit map of - 0-5 Page Selection in 32k increments - 6-7 Device selection: - 00 SSD off - 01 SSD 0 (Socket) - 10 SSD 1 (Flash chip) - 11 undefined - - On each SSD, the first 128k is reserved for use by the bios - (actually it IS the bios..) This only matters if you are booting off the - flash, you must not put a file system starting there. - - The driver tries to do a detection algorithm to guess what sort of devices - are plugged into the sockets. - - ##################################################################### */ - -#include -#include -#include -#include - -#include -#include - -#define WINDOW_START 0xe8000 -#define WINDOW_LENGTH 0x8000 -#define WINDOW_SHIFT 27 -#define WINDOW_MASK 0x7FFF -#define PAGE_IO 0x208 - -static volatile char page_n_dev = 0; -static unsigned long iomapadr; -static DEFINE_SPINLOCK(oct5066_spin); - -/* - * We use map_priv_1 to identify which device we are. - */ - -static void __oct5066_page(struct map_info *map, __u8 byte) -{ - outb(byte,PAGE_IO); - page_n_dev = byte; -} - -static inline void oct5066_page(struct map_info *map, unsigned long ofs) -{ - __u8 byte = map->map_priv_1 | (ofs >> WINDOW_SHIFT); - - if (page_n_dev != byte) - __oct5066_page(map, byte); -} - - -static map_word oct5066_read8(struct map_info *map, unsigned long ofs) -{ - map_word ret; - spin_lock(&oct5066_spin); - oct5066_page(map, ofs); - ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); - spin_unlock(&oct5066_spin); - return ret; -} - -static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - while(len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(from & WINDOW_MASK); - - spin_lock(&oct5066_spin); - oct5066_page(map, from); - memcpy_fromio(to, iomapadr + from, thislen); - spin_unlock(&oct5066_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr) -{ - spin_lock(&oct5066_spin); - oct5066_page(map, adr); - writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); - spin_unlock(&oct5066_spin); -} - -static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - while(len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(to & WINDOW_MASK); - - spin_lock(&oct5066_spin); - oct5066_page(map, to); - memcpy_toio(iomapadr + to, from, thislen); - spin_unlock(&oct5066_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static struct map_info oct5066_map[2] = { - { - .name = "Octagon 5066 Socket", - .phys = NO_XIP, - .size = 512 * 1024, - .bankwidth = 1, - .read = oct5066_read8, - .copy_from = oct5066_copy_from, - .write = oct5066_write8, - .copy_to = oct5066_copy_to, - .map_priv_1 = 1<<6 - }, - { - .name = "Octagon 5066 Internal Flash", - .phys = NO_XIP, - .size = 2 * 1024 * 1024, - .bankwidth = 1, - .read = oct5066_read8, - .copy_from = oct5066_copy_from, - .write = oct5066_write8, - .copy_to = oct5066_copy_to, - .map_priv_1 = 2<<6 - } -}; - -static struct mtd_info *oct5066_mtd[2] = {NULL, NULL}; - -// OctProbe - Sense if this is an octagon card -// --------------------------------------------------------------------- -/* Perform a simple validity test, we map the window select SSD0 and - change pages while monitoring the window. A change in the window, - controlled by the PAGE_IO port is a functioning 5066 board. This will - fail if the thing in the socket is set to a uniform value. */ -static int __init OctProbe(void) -{ - unsigned int Base = (1 << 6); - unsigned long I; - unsigned long Values[10]; - for (I = 0; I != 20; I++) - { - outb(Base + (I%10),PAGE_IO); - if (I < 10) - { - // Record the value and check for uniqueness - Values[I%10] = readl(iomapadr); - if (I > 0 && Values[I%10] == Values[0]) - return -EAGAIN; - } - else - { - // Make sure we get the same values on the second pass - if (Values[I%10] != readl(iomapadr)) - return -EAGAIN; - } - } - return 0; -} - -void cleanup_oct5066(void) -{ - int i; - for (i=0; i<2; i++) { - if (oct5066_mtd[i]) { - mtd_device_unregister(oct5066_mtd[i]); - map_destroy(oct5066_mtd[i]); - } - } - iounmap((void *)iomapadr); - release_region(PAGE_IO, 1); -} - -static int __init init_oct5066(void) -{ - int i; - int ret = 0; - - // Do an autoprobe sequence - if (!request_region(PAGE_IO,1,"Octagon SSD")) { - printk(KERN_NOTICE "5066: Page Register in Use\n"); - return -EAGAIN; - } - iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); - if (!iomapadr) { - printk(KERN_NOTICE "Failed to ioremap memory region\n"); - ret = -EIO; - goto out_rel; - } - if (OctProbe() != 0) { - printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n"); - iounmap((void *)iomapadr); - ret = -EAGAIN; - goto out_unmap; - } - - // Print out our little header.. - printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START, - WINDOW_START+WINDOW_LENGTH); - - for (i=0; i<2; i++) { - oct5066_mtd[i] = do_map_probe("cfi_probe", &oct5066_map[i]); - if (!oct5066_mtd[i]) - oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]); - if (!oct5066_mtd[i]) - oct5066_mtd[i] = do_map_probe("map_ram", &oct5066_map[i]); - if (!oct5066_mtd[i]) - oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]); - if (oct5066_mtd[i]) { - oct5066_mtd[i]->owner = THIS_MODULE; - mtd_device_register(oct5066_mtd[i], NULL, 0); - } - } - - if (!oct5066_mtd[0] && !oct5066_mtd[1]) { - cleanup_oct5066(); - return -ENXIO; - } - - return 0; - - out_unmap: - iounmap((void *)iomapadr); - out_rel: - release_region(PAGE_IO, 1); - return ret; -} - -module_init(init_oct5066); -module_exit(cleanup_oct5066); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jason Gunthorpe , David Woodhouse "); -MODULE_DESCRIPTION("MTD map driver for Octagon 5066 Single Board Computer"); diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c deleted file mode 100644 index 5e68de7..0000000 --- a/drivers/mtd/maps/vmax301.c +++ /dev/null @@ -1,196 +0,0 @@ -/* ###################################################################### - - Tempustech VMAX SBC301 MTD Driver. - - The VMAx 301 is a SBC based on . It - comes with three builtin AMD 29F016B flash chips and a socket for SRAM or - more flash. Each unit has it's own 8k mapping into a settable region - (0xD8000). There are two 8k mappings for each MTD, the first is always set - to the lower 8k of the device the second is paged. Writing a 16 bit page - value to anywhere in the first 8k will cause the second 8k to page around. - - To boot the device a bios extension must be installed into the first 8k - of flash that is smart enough to copy itself down, page in the rest of - itself and begin executing. - - ##################################################################### */ - -#include -#include -#include -#include -#include - -#include -#include - - -#define WINDOW_START 0xd8000 -#define WINDOW_LENGTH 0x2000 -#define WINDOW_SHIFT 25 -#define WINDOW_MASK 0x1FFF - -/* Actually we could use two spinlocks, but we'd have to have - more private space in the struct map_info. We lose a little - performance like this, but we'd probably lose more by having - the extra indirection from having one of the map->map_priv - fields pointing to yet another private struct. -*/ -static DEFINE_SPINLOCK(vmax301_spin); - -static void __vmax301_page(struct map_info *map, unsigned long page) -{ - writew(page, map->map_priv_2 - WINDOW_LENGTH); - map->map_priv_1 = page; -} - -static inline void vmax301_page(struct map_info *map, - unsigned long ofs) -{ - unsigned long page = (ofs >> WINDOW_SHIFT); - if (map->map_priv_1 != page) - __vmax301_page(map, page); -} - -static map_word vmax301_read8(struct map_info *map, unsigned long ofs) -{ - map_word ret; - spin_lock(&vmax301_spin); - vmax301_page(map, ofs); - ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK)); - spin_unlock(&vmax301_spin); - return ret; -} - -static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - while(len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(from & WINDOW_MASK); - spin_lock(&vmax301_spin); - vmax301_page(map, from); - memcpy_fromio(to, map->map_priv_2 + from, thislen); - spin_unlock(&vmax301_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr) -{ - spin_lock(&vmax301_spin); - vmax301_page(map, adr); - writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK)); - spin_unlock(&vmax301_spin); -} - -static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - while(len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(to & WINDOW_MASK); - - spin_lock(&vmax301_spin); - vmax301_page(map, to); - memcpy_toio(map->map_priv_2 + to, from, thislen); - spin_unlock(&vmax301_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static struct map_info vmax_map[2] = { - { - .name = "VMAX301 Internal Flash", - .phys = NO_XIP, - .size = 3*2*1024*1024, - .bankwidth = 1, - .read = vmax301_read8, - .copy_from = vmax301_copy_from, - .write = vmax301_write8, - .copy_to = vmax301_copy_to, - .map_priv_1 = WINDOW_START + WINDOW_LENGTH, - .map_priv_2 = 0xFFFFFFFF - }, - { - .name = "VMAX301 Socket", - .phys = NO_XIP, - .size = 0, - .bankwidth = 1, - .read = vmax301_read8, - .copy_from = vmax301_copy_from, - .write = vmax301_write8, - .copy_to = vmax301_copy_to, - .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH), - .map_priv_2 = 0xFFFFFFFF - } -}; - -static struct mtd_info *vmax_mtd[2] = {NULL, NULL}; - -static void __exit cleanup_vmax301(void) -{ - int i; - - for (i=0; i<2; i++) { - if (vmax_mtd[i]) { - mtd_device_unregister(vmax_mtd[i]); - map_destroy(vmax_mtd[i]); - } - } - iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START); -} - -static int __init init_vmax301(void) -{ - int i; - unsigned long iomapadr; - // Print out our little header.. - printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START, - WINDOW_START+4*WINDOW_LENGTH); - - iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4); - if (!iomapadr) { - printk("Failed to ioremap memory region\n"); - return -EIO; - } - /* Put the address in the map's private data area. - We store the actual MTD IO address rather than the - address of the first half, because it's used more - often. - */ - vmax_map[0].map_priv_2 = iomapadr + WINDOW_START; - vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START); - - for (i=0; i<2; i++) { - vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]); - if (!vmax_mtd[i]) - vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]); - if (!vmax_mtd[i]) - vmax_mtd[i] = do_map_probe("map_ram", &vmax_map[i]); - if (!vmax_mtd[i]) - vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]); - if (vmax_mtd[i]) { - vmax_mtd[i]->owner = THIS_MODULE; - mtd_device_register(vmax_mtd[i], NULL, 0); - } - } - - if (!vmax_mtd[0] && !vmax_mtd[1]) { - iounmap((void *)iomapadr); - return -ENXIO; - } - - return 0; -} - -module_init(init_vmax301); -module_exit(cleanup_vmax301); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse "); -MODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board"); -- cgit v0.10.2 From 96f2a4a1a77a60a55cc15e13938bd3f2d0ee86bc Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sun, 21 Jul 2013 14:52:02 -0300 Subject: mtd: nand: mxc_nand: Remove unneeded check for platform_get_resource() devm_ioremap_resource() checks its arguments, so there is no need for explicitly checking the return value from platform_get_resource(). Signed-off-by: Fabio Estevam Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 18855ad..0581bd3 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1446,8 +1446,6 @@ static int mxcnd_probe(struct platform_device *pdev) if (host->devtype_data->needs_ip) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; host->regs_ip = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->regs_ip)) return PTR_ERR(host->regs_ip); @@ -1457,9 +1455,6 @@ static int mxcnd_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); } - if (!res) - return -ENODEV; - host->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->base)) return PTR_ERR(host->base); -- cgit v0.10.2 From 9f7f7d8c062331cf4218e0ef2fa573913bad337f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 10 Jul 2013 16:57:41 +0100 Subject: mtd: gpmi-nand: don't use devm_pinctrl_get_select_default() in probe Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for setting the default pins. Compile tested only. Acked-by: Linus Walleij (personally at LCE13) Signed-off-by: Wolfram Sang Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index b741a6c..ef0978e 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -520,7 +519,6 @@ err_clock: static int acquire_resources(struct gpmi_nand_data *this) { - struct pinctrl *pinctrl; int ret; ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME); @@ -539,19 +537,12 @@ static int acquire_resources(struct gpmi_nand_data *this) if (ret) goto exit_dma_channels; - pinctrl = devm_pinctrl_get_select_default(&this->pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - goto exit_pin; - } - ret = gpmi_get_clks(this); if (ret) goto exit_clock; return 0; exit_clock: -exit_pin: release_dma_channels(this); exit_dma_channels: release_bch_irq(this); -- cgit v0.10.2 From 2e294972f0c3616df160f95604d1a75056b75a8a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 10 Jul 2013 16:57:40 +0100 Subject: mtd: atmel_nand: don't use devm_pinctrl_get_select_default() in probe Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for setting the default pins. Compile tested only. Acked-by: Linus Walleij (personally at LCE13) Signed-off-by: Wolfram Sang Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 0871510..73228e0 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -41,7 +41,6 @@ #include #include #include -#include static int use_dma = 1; module_param(use_dma, int, 0); @@ -1464,7 +1463,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev) struct resource *mem; struct mtd_part_parser_data ppdata = {}; int res; - struct pinctrl *pinctrl; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { @@ -1509,13 +1507,6 @@ 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; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - dev_err(host->dev, "Failed to request pinctrl\n"); - res = PTR_ERR(pinctrl); - goto err_ecc_ioremap; - } - if (gpio_is_valid(host->board.rdy_pin)) { res = gpio_request(host->board.rdy_pin, "nand_rdy"); if (res < 0) { -- cgit v0.10.2 From f9827dde2f6a1b64f8241de18ccf5027a636d8f4 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 8 Jul 2013 06:05:12 -0700 Subject: mtd: refactor call to request_module This reduces the size of the stack frame when calling request_module(). Performing the sprintf before the call is not needed. Signed-off-by: Kees Cook Acked-by: Rusty Russell Reviewed-by: James Hogan Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index 74dbb6b..ffb36ba 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -211,9 +211,7 @@ static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, probe_function = __symbol_get(probename); if (!probe_function) { - char modname[sizeof("cfi_cmdset_%4.4X")]; - sprintf(modname, "cfi_cmdset_%4.4X", type); - request_module(modname); + request_module("cfi_cmdset_%4.4X", type); probe_function = __symbol_get(probename); } -- cgit v0.10.2 From 9d40349681ea44d80ef84e7a9eb3786165ba79d1 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Mon, 8 Jul 2013 06:39:20 +0200 Subject: mtd: diskonchip: remove unused entries in Kconfig This patch proposes to remove kernel configuration parameters defined in drivers/mtd/devices/Kconfig, but used nowhere in the makefiles and source code (except in comments). Signed-off-by: Michael Opdenacker Acked-by: Kees Cook Acked-by: Robert Jarzmik Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 2a4d55e..74ab4b7 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -224,59 +224,4 @@ config BCH_CONST_T default 4 endif -config MTD_DOCPROBE - tristate - select MTD_DOCECC - -config MTD_DOCECC - tristate - -config MTD_DOCPROBE_ADVANCED - bool "Advanced detection options for DiskOnChip" - depends on MTD_DOCPROBE - help - This option allows you to specify nonstandard address at which to - probe for a DiskOnChip, or to change the detection options. You - are unlikely to need any of this unless you are using LinuxBIOS. - Say 'N'. - -config MTD_DOCPROBE_ADDRESS - hex "Physical address of DiskOnChip" if MTD_DOCPROBE_ADVANCED - depends on MTD_DOCPROBE - default "0x0" - ---help--- - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option allows you to specify a single address at which to probe - for the device, which is useful if you have other devices in that - range which get upset when they are probed. - - (Note that on PowerPC, the normal probe will only check at - 0xE4000000.) - - Normally, you should leave this set to zero, to allow the probe at - the normal addresses. - -config MTD_DOCPROBE_HIGH - bool "Probe high addresses" - depends on MTD_DOCPROBE_ADVANCED - help - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option changes to make it probe between 0xFFFC8000 and - 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be - useful to you. Say 'N'. - -config MTD_DOCPROBE_55AA - bool "Probe for 0x55 0xAA BIOS Extension Signature" - depends on MTD_DOCPROBE_ADVANCED - help - Check for the 0x55 0xAA signature of a DiskOnChip, and do not - continue with probing if it is absent. The signature will always be - present for a DiskOnChip 2000 or a normal DiskOnChip Millennium. - Only if you have overwritten the first block of a DiskOnChip - Millennium will it be absent. Enable this option if you are using - LinuxBIOS or if you need to recover a DiskOnChip Millennium on which - you have managed to wipe the first block. - endmenu diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 81fa578..eaa3c29 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -46,13 +46,13 @@ static unsigned long __initdata doc_locations[] = { 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, -#else /* CONFIG_MTD_DOCPROBE_HIGH */ +#else 0xc8000, 0xca000, 0xcc000, 0xce000, 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000, 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000, -#endif /* CONFIG_MTD_DOCPROBE_HIGH */ +#endif #endif 0xffffffff }; -- cgit v0.10.2 From 68e8078072e802e77134664f11d2ffbfbd2f8fbe Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 18 Jul 2013 01:17:02 -0700 Subject: mtd: nand: fix NAND_BUSWIDTH_AUTO for x16 devices The code for NAND_BUSWIDTH_AUTO is broken. According to Alexander: "I have a problem with attach NAND UBI in 16 bit mode. NAND works fine if I specify NAND_BUSWIDTH_16 option, but not working with NAND_BUSWIDTH_AUTO option. In second case NAND chip is identifyed with ONFI." See his report for the rest of the details: http://lists.infradead.org/pipermail/linux-mtd/2013-July/047515.html Anyway, the problem is that nand_set_defaults() is called twice, we intend it to reset the chip functions to their x16 buswidth verions if the buswidth changed from x8 to x16; however, nand_set_defaults() does exactly nothing if called a second time. Fix this by hacking nand_set_defaults() to reset the buswidth-dependent functions if they were set to the x8 version the first time. Note that this does not do anything to reset from x16 to x8, but that's not the supported use case for NAND_BUSWIDTH_AUTO anyway. Signed-off-by: Brian Norris Reported-by: Alexander Shiyan Tested-by: Alexander Shiyan Cc: Matthieu Castet Cc: # v3.8+ Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 1cbacff..0980ae3 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2786,7 +2786,9 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) if (!chip->select_chip) chip->select_chip = nand_select_chip; - if (!chip->read_byte) + + /* If called twice, pointers that depend on busw may need to be reset */ + if (!chip->read_byte || chip->read_byte == nand_read_byte) chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; if (!chip->read_word) chip->read_word = nand_read_word; @@ -2794,9 +2796,9 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) chip->block_bad = nand_block_bad; if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; - if (!chip->write_buf) + if (!chip->write_buf || chip->write_buf == nand_write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; - if (!chip->read_buf) + if (!chip->read_buf || chip->read_buf == nand_read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; -- cgit v0.10.2 From edc9cba495680a8a4968ed204b5c9f61a991bf4f Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 3 Jul 2013 17:56:19 +0800 Subject: mtd: atmel_nand: fix pmecc selction for ecc requirement typo Atmel PMECC support 2, 4, 8, 12, 24 bit error correction. So if the ecc requirement in ONFI is <= 2, 4, 8, 12, 24. We will use 2, 4, 8, 12, 24. This patch fix the typo. Use '<=' replace '<'. Reported-by: Scott Wood Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 73228e0..a43adce 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -959,11 +959,11 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host, host->pmecc_corr_cap = 2; else if (*cap <= 4) host->pmecc_corr_cap = 4; - else if (*cap < 8) + else if (*cap <= 8) host->pmecc_corr_cap = 8; - else if (*cap < 12) + else if (*cap <= 12) host->pmecc_corr_cap = 12; - else if (*cap < 24) + else if (*cap <= 24) host->pmecc_corr_cap = 24; else return -EINVAL; -- cgit v0.10.2 From fe2f4c8e0bf2756b670ee78fa9772613a2ea8495 Mon Sep 17 00:00:00 2001 From: Andrea Adami Date: Sun, 21 Jul 2013 01:07:33 +0200 Subject: mtd: jedec_probe: fix LH28F640BF definition Zaurus 5500 contains 2 LH28F640BFHE-PTTL90 (64M 4Mx16) and the LH28F640BFHE-PTTL90.pdf datasheet available on the net shows the exact erasesize and the OTP support. At the moment only jedec_probe can discover the chip and the NOR is mounted read only probably because of wrong vpp. Signed-off-by: Andrea Adami Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index c443f52..7c0b27d 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -120,7 +120,7 @@ #define PM49FL008 0x006A /* Sharp */ -#define LH28F640BF 0x00b0 +#define LH28F640BF 0x00B0 /* ST - www.st.com */ #define M29F800AB 0x0058 @@ -1299,13 +1299,14 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = CFI_MFR_SHARP, .dev_id = LH28F640BF, .name = "LH28F640BF", - .devtypes = CFI_DEVICETYPE_X8, + .devtypes = CFI_DEVICETYPE_X16, .uaddr = MTD_UADDR_UNNECESSARY, - .dev_size = SIZE_4MiB, - .cmd_set = P_ID_INTEL_STD, - .nr_regions = 1, + .dev_size = SIZE_8MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 2, .regions = { - ERASEINFO(0x40000,16), + ERASEINFO(0x10000, 127), + ERASEINFO(0x02000, 8), } }, { .mfr_id = CFI_MFR_SST, -- cgit v0.10.2 From 60c6738245612df9499b340c15edf48b8f3e7981 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 25 Jun 2013 13:17:59 -0700 Subject: mtd: nand: detect OOB size for Toshiba 24nm raw SLC Toshiba NAND datasheets have not been very forthcoming on OOB size information; they do not provide any bitfields in the ID string for spare area. In their 24nm technology flash, however, Toshiba migrated their NAND to have 32 bytes spare per 512 bytes of page area (up from the traditional 16 bytes), as they now require 8-bit ECC or higher. I have discussed this issue directly with Toshiba representatives, and they acknowledge this problem. They recommend detecting these flash based on their technology node as follows: For 24nm Toshiba SLC raw NAND (not BENAND -- Built-in Ecc NAND), there are 32 bytes of spare area for every 512 bytes of in-band data area. We can implement this rule with the following snippet of a device ID decode table, which applies to all their 43nm, 32nm, and 24nm SLC NAND (this table is not fully in the NAND datasheets, but it was provided directly by Toshiba representatives): - ID byte 5, bit[7]: 1 -> BENAND 0 -> raw SLC - ID byte 6, bits[2:0]: 100b -> 43nm 101b -> 32nm 110b -> 24nm 111b -> Reserved I'm also working with Toshiba on including this bitfield description for their 5th and 6th ID bytes in their public data sheets. I will provide the 8-byte ID strings from the two 24nm Toshiba samples I have; their first 6 bytes match the documentation I received from Toshiba: 24nm SLC 1Gbit TC58NVG0S3HTA00 0x98 0xf1 0x80 0x15 0x72 0x16 0x08 0x00 24nm SLC 2Gbit TC58NVG1S3HTA00 0x98 0xda 0x90 0x15 0x76 0x16 0x08 0x00 I have also tested for regressions with: 43nm SLC 4Gbit TC58NVG2S3ETA00 0x98 0xdc 0x90 0x15 0x76 0x14 0x03 0x10 32nm SLC 8Gbit TC58NVG3SOFA00 0x98 0xd3 0x90 0x26 0x76 0x15 0x02 0x08 Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0980ae3..158240d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3081,6 +3081,22 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, extid >>= 2; /* Get buswidth information */ *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + /* + * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per + * 512B page. For Toshiba SLC, we decode the 5th/6th byte as + * follows: + * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, + * 110b -> 24nm + * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC + */ + if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA && + !(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + (id_data[5] & 0x7) == 0x6 /* 24nm */ && + !(id_data[4] & 0x80) /* !BENAND */) { + mtd->oobsize = 32 * mtd->writesize >> 9; + } + } } -- cgit v0.10.2 From 08efe91a1befa67bcc3799e0d29b7194803e1538 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Jul 2013 11:21:53 +0900 Subject: mtd: nandsim: convert pages_written[] to bitmap nandsim.pages_written[] is the array of unsigned char which is indexed by the page number and used for identifying which pages have been written when cache_file is used. Each entry holds 0 (not written) or 1 (written), so it can be converted to bitmap. This reduces the allocation size of pages_written[] by 1/8. Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index cb38f3d..a73dce0 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -363,7 +363,7 @@ struct nandsim { /* Fields needed when using a cache file */ struct file *cfile; /* Open file */ - unsigned char *pages_written; /* Which pages have been written */ + unsigned long *pages_written; /* Which pages have been written */ void *file_buf; struct page *held_pages[NS_MAX_HELD_PAGES]; int held_cnt; @@ -586,7 +586,8 @@ static int alloc_device(struct nandsim *ns) err = -EINVAL; goto err_close; } - ns->pages_written = vzalloc(ns->geom.pgnum); + ns->pages_written = vzalloc(BITS_TO_LONGS(ns->geom.pgnum) * + sizeof(unsigned long)); if (!ns->pages_written) { NS_ERR("alloc_device: unable to allocate pages written array\n"); err = -ENOMEM; @@ -1479,7 +1480,7 @@ static void read_page(struct nandsim *ns, int num) union ns_mem *mypage; if (ns->cfile) { - if (!ns->pages_written[ns->regs.row]) { + if (!test_bit(ns->regs.row, ns->pages_written)) { NS_DBG("read_page: page %d not written\n", ns->regs.row); memset(ns->buf.byte, 0xFF, num); } else { @@ -1525,9 +1526,9 @@ static void erase_sector(struct nandsim *ns) if (ns->cfile) { for (i = 0; i < ns->geom.pgsec; i++) - if (ns->pages_written[ns->regs.row + i]) { + if (__test_and_clear_bit(ns->regs.row + i, + ns->pages_written)) { NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i); - ns->pages_written[ns->regs.row + i] = 0; } return; } @@ -1560,7 +1561,7 @@ static int prog_page(struct nandsim *ns, int num) NS_DBG("prog_page: writing page %d\n", ns->regs.row); pg_off = ns->file_buf + ns->regs.column + ns->regs.off; off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; - if (!ns->pages_written[ns->regs.row]) { + if (!test_bit(ns->regs.row, ns->pages_written)) { all = 1; memset(ns->file_buf, 0xff, ns->geom.pgszoob); } else { @@ -1580,7 +1581,7 @@ static int prog_page(struct nandsim *ns, int num) NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); return -1; } - ns->pages_written[ns->regs.row] = 1; + __set_bit(ns->regs.row, ns->pages_written); } else { tx = write_file(ns, ns->cfile, pg_off, num, off); if (tx != num) { -- cgit v0.10.2 From f03a5729287a8bc1eb061a2f849b5a33ed9cd3ab Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Jul 2013 11:21:54 +0900 Subject: mtd: nandsim: use kasprintf() Use kasprintf() which combines kmalloc and sprintf. Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index a73dce0..ac1b46c 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -654,9 +654,7 @@ static void free_device(struct nandsim *ns) static char *get_partition_name(int i) { - char buf[64]; - sprintf(buf, "NAND simulator partition %d", i); - return kstrdup(buf, GFP_KERNEL); + return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i); } /* -- cgit v0.10.2 From 3b8b8fa1ede2ea9f52a550fdddf2dda53ce52fc8 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Jul 2013 11:21:55 +0900 Subject: mtd: nandsim: simplify NS_RAW_OFFSET() Simplify the definision of NS_RAW_OFFSET() by using (ns)->geom.pgszoob which holds the sum of page size and OOB size. Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index ac1b46c..7aa7b19 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -205,7 +205,7 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " /* Calculate the page offset in flash RAM image by (row, column) address */ #define NS_RAW_OFFSET(ns) \ - (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column) + (((ns)->regs.row * (ns)->geom.pgszoob) + (ns)->regs.column) /* Calculate the OOB offset in flash RAM image by (row, column) address */ #define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) -- cgit v0.10.2 From 6d07fcf7e0e5da20120af247804f2cc9d2af82ac Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Jul 2013 11:21:56 +0900 Subject: mtd: nandsim: use NS_RAW_OFFSET() Use NS_RAW_OFFSET() to calculate the page offset in flash RAM image by (row, column) address. Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 7aa7b19..0ff53ef 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -1489,7 +1489,7 @@ static void read_page(struct nandsim *ns, int num) ns->regs.row, ns->regs.column + ns->regs.off); if (do_read_error(ns, num)) return; - pos = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; + pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos); if (tx != num) { NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); @@ -1558,7 +1558,7 @@ static int prog_page(struct nandsim *ns, int num) NS_DBG("prog_page: writing page %d\n", ns->regs.row); pg_off = ns->file_buf + ns->regs.column + ns->regs.off; - off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; + off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; if (!test_bit(ns->regs.row, ns->pages_written)) { all = 1; memset(ns->file_buf, 0xff, ns->geom.pgszoob); -- cgit v0.10.2 From 8e716a8b581a4700932a52ee89e981fa3339e0ff Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Jul 2013 11:21:57 +0900 Subject: mtd: nandsim: remove unused code Remove the leftover from commit 831d316b8b80b68dcdd2b3f5ede6d33c2bbf5835 ("mtd: nandsim: remove autoincrement code"). Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 0ff53ef..ce90990 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -760,12 +760,6 @@ static int init_nandsim(struct mtd_info *mtd) ns->nbparts += 1; } - /* Detect how many ID bytes the NAND chip outputs */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (second_id_byte != nand_flash_ids[i].dev_id) - continue; - } - if (ns->busw == 16) NS_WARN("16-bit flashes support wasn't tested\n"); -- cgit v0.10.2 From 2f3b07a7fcaac760cfefec6af123d52b2ea7c71e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Jul 2013 11:21:58 +0900 Subject: mtd: nandsim: remove unused ns->geom.oobshift ns->geom.oobshift holds bits number in OOB size, but OOB size is not always power of two. So it is useless and it actually isn't used in this driver except for just printing the value at module loading. Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index ce90990..9e04de0 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -336,7 +336,6 @@ struct nandsim { uint pgsec; /* number of pages per sector */ uint secshift; /* bits number in sector size */ uint pgshift; /* bits number in page size */ - uint oobshift; /* bits number in OOB size */ uint pgaddrbytes; /* bytes per page address */ uint secaddrbytes; /* bytes per sector address */ uint idbytes; /* the number ID bytes that this chip outputs */ @@ -689,7 +688,6 @@ static int init_nandsim(struct mtd_info *mtd) ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; ns->geom.secshift = ffs(ns->geom.secsz) - 1; ns->geom.pgshift = chip->page_shift; - ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; ns->options = 0; @@ -773,7 +771,7 @@ static int init_nandsim(struct mtd_info *mtd) printk("bus width: %u\n", ns->busw); printk("bits in sector size: %u\n", ns->geom.secshift); printk("bits in page size: %u\n", ns->geom.pgshift); - printk("bits in OOB size: %u\n", ns->geom.oobshift); + printk("bits in OOB size: %u\n", ffs(ns->geom.oobsz) - 1); printk("flash size with OOB: %llu KiB\n", (unsigned long long)ns->geom.totszoob >> 10); printk("page address bytes: %u\n", ns->geom.pgaddrbytes); -- cgit v0.10.2 From 0d63748d0b3f20852d999a79830f983c7c15db2e Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Mon, 5 Aug 2013 19:14:33 +0800 Subject: mtd: atmel_nand: use devm_xxx gpio kzalloc, gpio and ioremap this will allow to simply the error and remove path Cc: linux-mtd@lists.infradead.org Cc: Nicolas Ferre Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD [josh.wu@atmel.com: fix checkpatch warnings and rebase to latest mtd git tree] [josh.wu@atmel.com: replace devm_request_and_ioremap with devm_ioremap_resource] Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index a43adce..3d7db95 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -358,43 +358,34 @@ static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) table_size * sizeof(int16_t); } -static void pmecc_data_free(struct atmel_nand_host *host) -{ - kfree(host->pmecc_partial_syn); - kfree(host->pmecc_si); - kfree(host->pmecc_lmu); - kfree(host->pmecc_smu); - kfree(host->pmecc_mu); - kfree(host->pmecc_dmu); - kfree(host->pmecc_delta); -} - static int pmecc_data_alloc(struct atmel_nand_host *host) { const int cap = host->pmecc_corr_cap; + int size; + + size = (2 * cap + 1) * sizeof(int16_t); + host->pmecc_partial_syn = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_si = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_lmu = devm_kzalloc(host->dev, + (cap + 1) * sizeof(int16_t), GFP_KERNEL); + host->pmecc_smu = devm_kzalloc(host->dev, + (cap + 2) * size, GFP_KERNEL); + + size = (cap + 1) * sizeof(int); + host->pmecc_mu = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_dmu = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_delta = devm_kzalloc(host->dev, size, GFP_KERNEL); + + if (!host->pmecc_partial_syn || + !host->pmecc_si || + !host->pmecc_lmu || + !host->pmecc_smu || + !host->pmecc_mu || + !host->pmecc_dmu || + !host->pmecc_delta) + return -ENOMEM; - host->pmecc_partial_syn = kzalloc((2 * cap + 1) * sizeof(int16_t), - GFP_KERNEL); - host->pmecc_si = kzalloc((2 * cap + 1) * sizeof(int16_t), GFP_KERNEL); - host->pmecc_lmu = kzalloc((cap + 1) * sizeof(int16_t), GFP_KERNEL); - host->pmecc_smu = kzalloc((cap + 2) * (2 * cap + 1) * sizeof(int16_t), - GFP_KERNEL); - host->pmecc_mu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL); - host->pmecc_dmu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL); - host->pmecc_delta = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL); - - if (host->pmecc_partial_syn && - host->pmecc_si && - host->pmecc_lmu && - host->pmecc_smu && - host->pmecc_mu && - host->pmecc_dmu && - host->pmecc_delta) - return 0; - - /* error happened */ - pmecc_data_free(host); - return -ENOMEM; + return 0; } static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector) @@ -1015,27 +1006,28 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, return 0; } - host->ecc = ioremap(regs->start, resource_size(regs)); - if (host->ecc == NULL) { + host->ecc = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(host->ecc)) { dev_err(host->dev, "ioremap failed\n"); - err_no = -EIO; - goto err_pmecc_ioremap; + err_no = PTR_ERR(host->ecc); + goto err; } regs_pmerr = platform_get_resource(pdev, IORESOURCE_MEM, 2); - regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3); - if (regs_pmerr && regs_rom) { - host->pmerrloc_base = ioremap(regs_pmerr->start, - resource_size(regs_pmerr)); - host->pmecc_rom_base = ioremap(regs_rom->start, - resource_size(regs_rom)); + host->pmerrloc_base = devm_ioremap_resource(&pdev->dev, regs_pmerr); + if (IS_ERR(host->pmerrloc_base)) { + dev_err(host->dev, + "Can not get I/O resource for PMECC ERRLOC controller!\n"); + err_no = PTR_ERR(host->pmerrloc_base); + goto err; } - if (!host->pmerrloc_base || !host->pmecc_rom_base) { - dev_err(host->dev, - "Can not get I/O resource for PMECC ERRLOC controller or ROM!\n"); - err_no = -EIO; - goto err_pmloc_ioremap; + regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3); + host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev, regs_rom); + if (IS_ERR(host->pmecc_rom_base)) { + dev_err(host->dev, "Can not get I/O resource for ROM!\n"); + err_no = PTR_ERR(host->pmecc_rom_base); + goto err; } /* ECC is calculated for the whole page (1 step) */ @@ -1060,7 +1052,7 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, if (nand_chip->ecc.bytes > mtd->oobsize - 2) { dev_err(host->dev, "No room for ECC bytes\n"); err_no = -EINVAL; - goto err_no_ecc_room; + goto err; } pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, mtd->oobsize, @@ -1085,7 +1077,7 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, if (err_no) { dev_err(host->dev, "Cannot allocate memory for PMECC computation!\n"); - goto err_pmecc_data_alloc; + goto err; } nand_chip->ecc.read_page = atmel_nand_pmecc_read_page; @@ -1095,15 +1087,7 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, return 0; -err_pmecc_data_alloc: -err_no_ecc_room: -err_pmloc_ioremap: - iounmap(host->ecc); - if (host->pmerrloc_base) - iounmap(host->pmerrloc_base); - if (host->pmecc_rom_base) - iounmap(host->pmecc_rom_base); -err_pmecc_ioremap: +err: return err_no; } @@ -1407,10 +1391,10 @@ static int __init atmel_hw_nand_init_params(struct platform_device *pdev, return 0; } - host->ecc = ioremap(regs->start, resource_size(regs)); - if (host->ecc == NULL) { + host->ecc = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(host->ecc)) { dev_err(host->dev, "ioremap failed\n"); - return -EIO; + return PTR_ERR(host->ecc); } /* ECC is calculated for the whole page (1 step) */ @@ -1464,27 +1448,21 @@ static int __init atmel_nand_probe(struct platform_device *pdev) struct mtd_part_parser_data ppdata = {}; int res; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - printk(KERN_ERR "atmel_nand: can't get I/O resource mem\n"); - return -ENXIO; - } - /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(struct atmel_nand_host), GFP_KERNEL); + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) { printk(KERN_ERR "atmel_nand: failed to allocate device structure.\n"); return -ENOMEM; } - host->io_phys = (dma_addr_t)mem->start; - - host->io_base = ioremap(mem->start, resource_size(mem)); - if (host->io_base == NULL) { - printk(KERN_ERR "atmel_nand: ioremap failed\n"); - res = -EIO; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->io_base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(host->io_base)) { + dev_err(&pdev->dev, "atmel_nand: ioremap resource failed\n"); + res = PTR_ERR(host->io_base); goto err_nand_ioremap; } + host->io_phys = (dma_addr_t)mem->start; mtd = &host->mtd; nand_chip = &host->nand_chip; @@ -1492,7 +1470,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if (pdev->dev.of_node) { res = atmel_of_init_port(host, pdev->dev.of_node); if (res) - goto err_ecc_ioremap; + goto err_nand_ioremap; } else { memcpy(&host->board, pdev->dev.platform_data, sizeof(struct atmel_nand_data)); @@ -1508,12 +1486,13 @@ static int __init atmel_nand_probe(struct platform_device *pdev) nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; if (gpio_is_valid(host->board.rdy_pin)) { - res = gpio_request(host->board.rdy_pin, "nand_rdy"); + res = devm_gpio_request(&pdev->dev, + host->board.rdy_pin, "nand_rdy"); if (res < 0) { dev_err(&pdev->dev, "can't request rdy gpio %d\n", host->board.rdy_pin); - goto err_ecc_ioremap; + goto err_nand_ioremap; } res = gpio_direction_input(host->board.rdy_pin); @@ -1521,19 +1500,20 @@ static int __init atmel_nand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "can't request input direction rdy gpio %d\n", host->board.rdy_pin); - goto err_ecc_ioremap; + goto err_nand_ioremap; } nand_chip->dev_ready = atmel_nand_device_ready; } if (gpio_is_valid(host->board.enable_pin)) { - res = gpio_request(host->board.enable_pin, "nand_enable"); + res = devm_gpio_request(&pdev->dev, + host->board.enable_pin, "nand_enable"); if (res < 0) { dev_err(&pdev->dev, "can't request enable gpio %d\n", host->board.enable_pin); - goto err_ecc_ioremap; + goto err_nand_ioremap; } res = gpio_direction_output(host->board.enable_pin, 1); @@ -1541,7 +1521,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "can't request output direction enable gpio %d\n", host->board.enable_pin); - goto err_ecc_ioremap; + goto err_nand_ioremap; } } @@ -1558,7 +1538,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev) atmel_nand_enable(host); if (gpio_is_valid(host->board.det_pin)) { - res = gpio_request(host->board.det_pin, "nand_det"); + res = devm_gpio_request(&pdev->dev, + host->board.det_pin, "nand_det"); if (res < 0) { dev_err(&pdev->dev, "can't request det gpio %d\n", @@ -1636,26 +1617,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev) return res; err_scan_tail: - if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) { + if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); - pmecc_data_free(host); - } - if (host->ecc) - iounmap(host->ecc); - if (host->pmerrloc_base) - iounmap(host->pmerrloc_base); - if (host->pmecc_rom_base) - iounmap(host->pmecc_rom_base); err_hw_ecc: err_scan_ident: err_no_card: atmel_nand_disable(host); if (host->dma_chan) dma_release_channel(host->dma_chan); -err_ecc_ioremap: - iounmap(host->io_base); err_nand_ioremap: - kfree(host); return res; } @@ -1675,31 +1645,11 @@ static int __exit atmel_nand_remove(struct platform_device *pdev) pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); pmerrloc_writel(host->pmerrloc_base, ELDIS, PMERRLOC_DISABLE); - pmecc_data_free(host); } - if (gpio_is_valid(host->board.det_pin)) - gpio_free(host->board.det_pin); - - if (gpio_is_valid(host->board.enable_pin)) - gpio_free(host->board.enable_pin); - - if (gpio_is_valid(host->board.rdy_pin)) - gpio_free(host->board.rdy_pin); - - if (host->ecc) - iounmap(host->ecc); - if (host->pmecc_rom_base) - iounmap(host->pmecc_rom_base); - if (host->pmerrloc_base) - iounmap(host->pmerrloc_base); - if (host->dma_chan) dma_release_channel(host->dma_chan); - iounmap(host->io_base); - kfree(host); - return 0; } -- cgit v0.10.2 From 5ee3d9da11c0ddf9c641f70da525d9b3a23b9bd4 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Mon, 5 Aug 2013 19:14:34 +0800 Subject: mtd: atmel_nand: replace pmecc enable code with one function. Signed-off-by: Josh Wu Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 3d7db95..28d159a 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -746,6 +746,30 @@ normal_check: return total_err; } +static void pmecc_enable(struct atmel_nand_host *host, int ecc_op) +{ + u32 val; + + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); + val = pmecc_readl_relaxed(host->ecc, CFG); + + if (ecc_op != NAND_ECC_READ && ecc_op != NAND_ECC_WRITE) { + dev_err(host->dev, "atmel_nand: wrong pmecc operation type!"); + return; + } + + if (ecc_op == NAND_ECC_READ) + pmecc_writel(host->ecc, CFG, (val & ~PMECC_CFG_WRITE_OP) + | PMECC_CFG_AUTO_ENABLE); + else + pmecc_writel(host->ecc, CFG, (val | PMECC_CFG_WRITE_OP) + & ~PMECC_CFG_AUTO_ENABLE); + + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA); +} + static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { @@ -757,13 +781,7 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, unsigned long end_time; int bitflips = 0; - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); - pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG) - & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE); - - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA); + pmecc_enable(host, NAND_ECC_READ); chip->read_buf(mtd, buf, eccsize); chip->read_buf(mtd, oob, mtd->oobsize); @@ -796,14 +814,7 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, int i, j; unsigned long end_time; - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); - - pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG) | - PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE); - - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA); + pmecc_enable(host, NAND_ECC_WRITE); chip->write_buf(mtd, (u8 *)buf, mtd->writesize); -- cgit v0.10.2 From 7dc37de7d59cdd1c4cdd472e1994c31b16d7b874 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Mon, 5 Aug 2013 19:14:35 +0800 Subject: mtd: atmel_nand: add Nand Flash Controller (NFC) support Nand Flash Controller (NFC) can handle automatic transfers, sending the commands and address cycles to the NAND Flash. To use NFC in this driver, user needs to add NFC child node in nand flash driver. The NFC child node includes NFC's compatible string and regiters of the address and size of NFC command registers, NFC registers (embedded in HSMC) and NFC SRAM. Also user need to set up the HSMC irq, which use to check whether nfc command is finish or not. This driver has been tested on SAMA5D3X-EK board with JFFS2, YAFFS, UBIFS and mtd-utils. I put the part of the mtd_speedtest result here for your information. >From the mtd_speedtest, we can see the NFC will reduce the %50 of cpu load when writing nand flash. No change when reading. In the meantime, the speed will be slow about %8. - commands use to test: #insmod /mnt/mtd_speedtest.ko dev=2 & #top -n 30 -d 1 | grep speedtest - test result: Before the patch: ================================================= mtd_speedtest: MTD device: 2 mtd_speedtest: MTD device size 41943040, eraseblock size 131072, page size 2048, count of eraseblocks 320, pages per eraseblock 64, OOB size 64 515 495 root R 1164 0% 93% insmod /mnt/mtd_speedtest.ko dev=2 515 495 root R 1164 0% 98% insmod /mnt/mtd_speedtest.ko dev=2 515 495 root R 1164 0% 99% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: eraseblock write speed is 5768 KiB/s mtd_speedtest: testing eraseblock read speed 515 495 root R 1164 0% 92% insmod /mnt/mtd_speedtest.ko dev=2 515 495 root R 1164 0% 91% insmod /mnt/mtd_speedtest.ko dev=2 515 495 root R 1164 0% 94% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: eraseblock read speed is 5932 KiB/s mtd_speedtest: testing page write speed 515 495 root R 1164 0% 94% insmod /mnt/mtd_speedtest.ko dev=2 515 495 root R 1164 0% 98% insmod /mnt/mtd_speedtest.ko dev=2 515 495 root R 1164 0% 98% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: page write speed is 5770 KiB/s mtd_speedtest: testing page read speed 515 495 root R 1164 0% 91% insmod /mnt/mtd_speedtest.ko dev=2 515 495 root R 1164 0% 89% insmod /mnt/mtd_speedtest.ko dev=2 515 495 root R 1164 0% 91% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: page read speed is 5910 KiB/s After the patch: ================================================= mtd_speedtest: MTD device: 2 mtd_speedtest: MTD device size 41943040, eraseblock size 131072, page size 2048, count of eraseblocks 320, pages per eraseblock 64, OOB size 64 mtd_speedtest: testing eraseblock write speed 509 495 root D 1164 0% 49% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 50% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 47% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: eraseblock write speed is 5370 KiB/s mtd_speedtest: testing eraseblock read speed 509 495 root R 1164 0% 92% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root R 1164 0% 91% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root R 1164 0% 95% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: eraseblock read speed is 5715 KiB/s mtd_speedtest: testing page write speed 509 495 root D 1164 0% 48% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 47% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 50% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: page write speed is 5224 KiB/s mtd_speedtest: testing page read speed 509 495 root R 1164 0% 89% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root R 1164 0% 94% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root R 1164 0% 93% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: page read speed is 5641 KiB/s Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt index b6eb484..5d8b7d5 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -30,6 +30,12 @@ Optional properties: sector size 1024. - nand-bus-width : 8 or 16 bus width if not present 8 - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false +- Nand Flash Controller(NFC) is a slave driver under Atmel nand flash + - Required properties: + - compatible : "atmel,sama5d3-nfc". + - reg : should specify the address and size used for NFC command registers, + NFC registers and NFC Sram. NFC Sram address and size can be absent + if don't want to use it. Examples: nand0: nand@40000000,0 { @@ -78,3 +84,22 @@ nand0: nand@40000000 { ... }; }; + +/* for NFC supported chips */ +nand0: nand@40000000 { + compatible = "atmel,at91rm9200-nand"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + ... + nfc@70000000 { + compatible = "atmel,sama5d3-nfc"; + #address-cells = <1>; + #size-cells = <1>; + reg = < + 0x70000000 0x10000000 /* NFC Command Registers */ + 0xffffc000 0x00000070 /* NFC HSMC regs */ + 0x00200000 0x00100000 /* NFC SRAM banks */ + >; + }; +}; diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 28d159a..9e0f95d 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -18,6 +18,9 @@ * Add Programmable Multibit ECC support for various AT91 SoC * © Copyright 2012 ATMEL, Hong Xu * + * Add Nand Flash Controller support for SAMA5 SoC + * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -37,8 +40,10 @@ #include #include +#include #include #include +#include #include #include @@ -55,6 +60,7 @@ module_param(on_flash_bbt, int, 0); __raw_writel((value), add + ATMEL_ECC_##reg) #include "atmel_nand_ecc.h" /* Hardware ECC registers */ +#include "atmel_nand_nfc.h" /* Nand Flash Controller definition */ /* oob layout for large page size * bad block info is on bytes 0 and 1 @@ -82,6 +88,17 @@ static struct nand_ecclayout atmel_oobinfo_small = { }, }; +struct atmel_nfc { + void __iomem *base_cmd_regs; + void __iomem *hsmc_regs; + void __iomem *sram_bank0; + dma_addr_t sram_bank0_phys; + + bool is_initialized; + struct completion comp_nfc; +}; +static struct atmel_nfc nand_nfc; + struct atmel_nand_host { struct nand_chip nand_chip; struct mtd_info mtd; @@ -94,6 +111,8 @@ struct atmel_nand_host { struct completion comp; struct dma_chan *dma_chan; + struct atmel_nfc *nfc; + bool has_pmecc; u8 pmecc_corr_cap; u16 pmecc_sector_size; @@ -178,6 +197,56 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) !!host->board.rdy_pin_active_low; } +/* Set up for hardware ready pin and enable pin. */ +static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + int res = 0; + + if (gpio_is_valid(host->board.rdy_pin)) { + res = devm_gpio_request(host->dev, + host->board.rdy_pin, "nand_rdy"); + if (res < 0) { + dev_err(host->dev, + "can't request rdy gpio %d\n", + host->board.rdy_pin); + return res; + } + + res = gpio_direction_input(host->board.rdy_pin); + if (res < 0) { + dev_err(host->dev, + "can't request input direction rdy gpio %d\n", + host->board.rdy_pin); + return res; + } + + chip->dev_ready = atmel_nand_device_ready; + } + + if (gpio_is_valid(host->board.enable_pin)) { + res = devm_gpio_request(host->dev, + host->board.enable_pin, "nand_enable"); + if (res < 0) { + dev_err(host->dev, + "can't request enable gpio %d\n", + host->board.enable_pin); + return res; + } + + res = gpio_direction_output(host->board.enable_pin, 1); + if (res < 0) { + dev_err(host->dev, + "can't request output direction enable gpio %d\n", + host->board.enable_pin); + return res; + } + } + + return res; +} + /* * Minimal-overhead PIO for data access. */ @@ -1336,6 +1405,9 @@ static int atmel_of_init_port(struct atmel_nand_host *host, host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc"); + /* load the nfc driver if there is */ + of_platform_populate(np, NULL, NULL, host->dev); + if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc) return 0; /* Not using PMECC */ @@ -1447,6 +1519,239 @@ static int __init atmel_hw_nand_init_params(struct platform_device *pdev, return 0; } +/* SMC interrupt service routine */ +static irqreturn_t hsmc_interrupt(int irq, void *dev_id) +{ + struct atmel_nand_host *host = dev_id; + u32 status, mask, pending; + irqreturn_t ret = IRQ_HANDLED; + + status = nfc_readl(host->nfc->hsmc_regs, SR); + mask = nfc_readl(host->nfc->hsmc_regs, IMR); + pending = status & mask; + + if (pending & NFC_SR_XFR_DONE) { + complete(&host->nfc->comp_nfc); + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE); + } else if (pending & NFC_SR_RB_EDGE) { + complete(&host->nfc->comp_nfc); + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_RB_EDGE); + } else if (pending & NFC_SR_CMD_DONE) { + complete(&host->nfc->comp_nfc); + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_CMD_DONE); + } else { + ret = IRQ_NONE; + } + + return ret; +} + +/* NFC(Nand Flash Controller) related functions */ +static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag) +{ + unsigned long timeout; + init_completion(&host->nfc->comp_nfc); + + /* Enable interrupt that need to wait for */ + nfc_writel(host->nfc->hsmc_regs, IER, flag); + + timeout = wait_for_completion_timeout(&host->nfc->comp_nfc, + msecs_to_jiffies(NFC_TIME_OUT_MS)); + if (timeout) + return 0; + + /* Time out to wait for the interrupt */ + dev_err(host->dev, "Time out to wait for interrupt: 0x%08x\n", flag); + return -ETIMEDOUT; +} + +static int nfc_send_command(struct atmel_nand_host *host, + unsigned int cmd, unsigned int addr, unsigned char cycle0) +{ + unsigned long timeout; + dev_dbg(host->dev, + "nfc_cmd: 0x%08x, addr1234: 0x%08x, cycle0: 0x%02x\n", + cmd, addr, cycle0); + + timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS); + while (nfc_cmd_readl(NFCADDR_CMD_NFCBUSY, host->nfc->base_cmd_regs) + & NFCADDR_CMD_NFCBUSY) { + if (time_after(jiffies, timeout)) { + dev_err(host->dev, + "Time out to wait CMD_NFCBUSY ready!\n"); + return -ETIMEDOUT; + } + } + nfc_writel(host->nfc->hsmc_regs, CYCLE0, cycle0); + nfc_cmd_addr1234_writel(cmd, addr, host->nfc->base_cmd_regs); + return nfc_wait_interrupt(host, NFC_SR_CMD_DONE); +} + +static int nfc_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + if (!nfc_wait_interrupt(host, NFC_SR_RB_EDGE)) + return 1; + return 0; +} + +static void nfc_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + + if (chip == -1) + nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_DISABLE); + else + nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE); +} + +static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr, + unsigned int *addr1234, unsigned int *cycle0) +{ + struct nand_chip *chip = mtd->priv; + + int acycle = 0; + unsigned char addr_bytes[8]; + int index = 0, bit_shift; + + BUG_ON(addr1234 == NULL || cycle0 == NULL); + + *cycle0 = 0; + *addr1234 = 0; + + if (column != -1) { + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + addr_bytes[acycle++] = column & 0xff; + if (mtd->writesize > 512) + addr_bytes[acycle++] = (column >> 8) & 0xff; + } + + if (page_addr != -1) { + addr_bytes[acycle++] = page_addr & 0xff; + addr_bytes[acycle++] = (page_addr >> 8) & 0xff; + if (chip->chipsize > (128 << 20)) + addr_bytes[acycle++] = (page_addr >> 16) & 0xff; + } + + if (acycle > 4) + *cycle0 = addr_bytes[index++]; + + for (bit_shift = 0; index < acycle; bit_shift += 8) + *addr1234 += addr_bytes[index++] << bit_shift; + + /* return acycle in cmd register */ + return acycle << NFCADDR_CMD_ACYCLE_BIT_POS; +} + +static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + unsigned long timeout; + unsigned int nfc_addr_cmd = 0; + + unsigned int cmd1 = command << NFCADDR_CMD_CMD1_BIT_POS; + + /* Set default settings: no cmd2, no addr cycle. read from nand */ + unsigned int cmd2 = 0; + unsigned int vcmd2 = 0; + int acycle = NFCADDR_CMD_ACYCLE_NONE; + int csid = NFCADDR_CMD_CSID_3; + int dataen = NFCADDR_CMD_DATADIS; + int nfcwr = NFCADDR_CMD_NFCRD; + unsigned int addr1234 = 0; + unsigned int cycle0 = 0; + bool do_addr = true; + + dev_dbg(host->dev, "%s: cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n", + __func__, command, column, page_addr); + + switch (command) { + case NAND_CMD_RESET: + nfc_addr_cmd = cmd1 | acycle | csid | dataen | nfcwr; + nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0); + udelay(chip->chip_delay); + + nfc_nand_command(mtd, NAND_CMD_STATUS, -1, -1); + timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) { + if (time_after(jiffies, timeout)) { + dev_err(host->dev, + "Time out to wait status ready!\n"); + break; + } + } + return; + case NAND_CMD_STATUS: + do_addr = false; + break; + case NAND_CMD_PARAM: + case NAND_CMD_READID: + do_addr = false; + acycle = NFCADDR_CMD_ACYCLE_1; + if (column != -1) + addr1234 = column; + break; + case NAND_CMD_RNDOUT: + cmd2 = NAND_CMD_RNDOUTSTART << NFCADDR_CMD_CMD2_BIT_POS; + vcmd2 = NFCADDR_CMD_VCMD2; + break; + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; /* only READ0 is valid */ + cmd1 = command << NFCADDR_CMD_CMD1_BIT_POS; + } + + cmd2 = NAND_CMD_READSTART << NFCADDR_CMD_CMD2_BIT_POS; + vcmd2 = NFCADDR_CMD_VCMD2; + break; + /* For prgramming command, the cmd need set to write enable */ + case NAND_CMD_PAGEPROG: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + nfcwr = NFCADDR_CMD_NFCWR; + break; + default: + break; + } + + if (do_addr) + acycle = nfc_make_addr(mtd, column, page_addr, &addr1234, + &cycle0); + + nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr; + nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0); + + /* + * Program and erase have their own busy handlers status, sequential + * in, and deplete1 need no delay. + */ + switch (command) { + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_RNDOUT: + case NAND_CMD_SEQIN: + case NAND_CMD_READID: + return; + + case NAND_CMD_READ0: + /* fall through */ + default: + nfc_wait_interrupt(host, NFC_SR_RB_EDGE); + } +} + +static struct platform_driver atmel_nand_nfc_driver; /* * Probe for the NAND device. */ @@ -1457,7 +1762,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) struct nand_chip *nand_chip; struct resource *mem; struct mtd_part_parser_data ppdata = {}; - int res; + int res, irq; /* Allocate memory for the device structure (and zero it) */ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); @@ -1466,6 +1771,10 @@ static int __init atmel_nand_probe(struct platform_device *pdev) return -ENOMEM; } + res = platform_driver_register(&atmel_nand_nfc_driver); + if (res) + dev_err(&pdev->dev, "atmel_nand: can't register NFC driver\n"); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->io_base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(host->io_base)) { @@ -1494,46 +1803,35 @@ static int __init atmel_nand_probe(struct platform_device *pdev) /* Set address of NAND IO lines */ nand_chip->IO_ADDR_R = host->io_base; nand_chip->IO_ADDR_W = host->io_base; - nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; - - if (gpio_is_valid(host->board.rdy_pin)) { - res = devm_gpio_request(&pdev->dev, - host->board.rdy_pin, "nand_rdy"); - if (res < 0) { - dev_err(&pdev->dev, - "can't request rdy gpio %d\n", - host->board.rdy_pin); - goto err_nand_ioremap; - } - res = gpio_direction_input(host->board.rdy_pin); - if (res < 0) { - dev_err(&pdev->dev, - "can't request input direction rdy gpio %d\n", - host->board.rdy_pin); - goto err_nand_ioremap; - } + if (nand_nfc.is_initialized) { + /* NFC driver is probed and initialized */ + host->nfc = &nand_nfc; - nand_chip->dev_ready = atmel_nand_device_ready; - } + nand_chip->select_chip = nfc_select_chip; + nand_chip->dev_ready = nfc_device_ready; + nand_chip->cmdfunc = nfc_nand_command; - if (gpio_is_valid(host->board.enable_pin)) { - res = devm_gpio_request(&pdev->dev, - host->board.enable_pin, "nand_enable"); - if (res < 0) { - dev_err(&pdev->dev, - "can't request enable gpio %d\n", - host->board.enable_pin); + /* Initialize the interrupt for NFC */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(host->dev, "Cannot get HSMC irq!\n"); goto err_nand_ioremap; } - res = gpio_direction_output(host->board.enable_pin, 1); - if (res < 0) { - dev_err(&pdev->dev, - "can't request output direction enable gpio %d\n", - host->board.enable_pin); + res = devm_request_irq(&pdev->dev, irq, hsmc_interrupt, + 0, "hsmc", host); + if (res) { + dev_err(&pdev->dev, "Unable to request HSMC irq %d\n", + irq); goto err_nand_ioremap; } + } else { + res = atmel_nand_set_enable_ready_pins(mtd); + if (res) + goto err_nand_ioremap; + + nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; } nand_chip->ecc.mode = host->board.ecc_mode; @@ -1637,6 +1935,7 @@ err_no_card: if (host->dma_chan) dma_release_channel(host->dma_chan); err_nand_ioremap: + platform_driver_unregister(&atmel_nand_nfc_driver); return res; } @@ -1661,6 +1960,8 @@ static int __exit atmel_nand_remove(struct platform_device *pdev) if (host->dma_chan) dma_release_channel(host->dma_chan); + platform_driver_unregister(&atmel_nand_nfc_driver); + return 0; } @@ -1673,6 +1974,50 @@ static const struct of_device_id atmel_nand_dt_ids[] = { MODULE_DEVICE_TABLE(of, atmel_nand_dt_ids); #endif +static int atmel_nand_nfc_probe(struct platform_device *pdev) +{ + struct atmel_nfc *nfc = &nand_nfc; + struct resource *nfc_cmd_regs, *nfc_hsmc_regs, *nfc_sram; + + nfc_cmd_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->base_cmd_regs = devm_ioremap_resource(&pdev->dev, nfc_cmd_regs); + if (IS_ERR(nfc->base_cmd_regs)) + return PTR_ERR(nfc->base_cmd_regs); + + nfc_hsmc_regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + nfc->hsmc_regs = devm_ioremap_resource(&pdev->dev, nfc_hsmc_regs); + if (IS_ERR(nfc->hsmc_regs)) + return PTR_ERR(nfc->hsmc_regs); + + nfc_sram = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (nfc_sram) { + nfc->sram_bank0 = devm_ioremap_resource(&pdev->dev, nfc_sram); + if (IS_ERR(nfc->sram_bank0)) + dev_warn(&pdev->dev, "Fail to ioremap the NFC sram with error: %ld. So disable NFC sram.\n", + PTR_ERR(nfc->sram_bank0)); + else + nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start; + } + + nfc->is_initialized = true; + dev_info(&pdev->dev, "NFC is probed.\n"); + return 0; +} + +static struct of_device_id atmel_nand_nfc_match[] = { + { .compatible = "atmel,sama5d3-nfc" }, + { /* sentinel */ } +}; + +static struct platform_driver atmel_nand_nfc_driver = { + .driver = { + .name = "atmel_nand_nfc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_nand_nfc_match), + }, + .probe = atmel_nand_nfc_probe, +}; + static struct platform_driver atmel_nand_driver = { .remove = __exit_p(atmel_nand_remove), .driver = { diff --git a/drivers/mtd/nand/atmel_nand_nfc.h b/drivers/mtd/nand/atmel_nand_nfc.h new file mode 100644 index 0000000..4efd117 --- /dev/null +++ b/drivers/mtd/nand/atmel_nand_nfc.h @@ -0,0 +1,98 @@ +/* + * Atmel Nand Flash Controller (NFC) - System peripherals regsters. + * Based on SAMA5D3 datasheet. + * + * © Copyright 2013 Atmel Corporation. + * + * 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. + */ + +#ifndef ATMEL_NAND_NFC_H +#define ATMEL_NAND_NFC_H + +/* + * HSMC NFC registers + */ +#define ATMEL_HSMC_NFC_CFG 0x00 /* NFC Configuration Register */ +#define NFC_CFG_PAGESIZE (7 << 0) +#define NFC_CFG_PAGESIZE_512 (0 << 0) +#define NFC_CFG_PAGESIZE_1024 (1 << 0) +#define NFC_CFG_PAGESIZE_2048 (2 << 0) +#define NFC_CFG_PAGESIZE_4096 (3 << 0) +#define NFC_CFG_PAGESIZE_8192 (4 << 0) +#define NFC_CFG_WSPARE (1 << 8) +#define NFC_CFG_RSPARE (1 << 9) +#define NFC_CFG_NFC_DTOCYC (0xf << 16) +#define NFC_CFG_NFC_DTOMUL (0x7 << 20) +#define NFC_CFG_NFC_SPARESIZE (0x7f << 24) +#define NFC_CFG_NFC_SPARESIZE_BIT_POS 24 + +#define ATMEL_HSMC_NFC_CTRL 0x04 /* NFC Control Register */ +#define NFC_CTRL_ENABLE (1 << 0) +#define NFC_CTRL_DISABLE (1 << 1) + +#define ATMEL_HSMC_NFC_SR 0x08 /* NFC Status Register */ +#define NFC_SR_XFR_DONE (1 << 16) +#define NFC_SR_CMD_DONE (1 << 17) +#define NFC_SR_RB_EDGE (1 << 24) + +#define ATMEL_HSMC_NFC_IER 0x0c +#define ATMEL_HSMC_NFC_IDR 0x10 +#define ATMEL_HSMC_NFC_IMR 0x14 +#define ATMEL_HSMC_NFC_CYCLE0 0x18 /* NFC Address Cycle Zero */ +#define ATMEL_HSMC_NFC_ADDR_CYCLE0 (0xff) + +#define ATMEL_HSMC_NFC_BANK 0x1c /* NFC Bank Register */ +#define ATMEL_HSMC_NFC_BANK0 (0 << 0) +#define ATMEL_HSMC_NFC_BANK1 (1 << 0) + +#define nfc_writel(addr, reg, value) \ + writel((value), (addr) + ATMEL_HSMC_NFC_##reg) + +#define nfc_readl(addr, reg) \ + readl_relaxed((addr) + ATMEL_HSMC_NFC_##reg) + +/* + * NFC Address Command definitions + */ +#define NFCADDR_CMD_CMD1 (0xff << 2) /* Command for Cycle 1 */ +#define NFCADDR_CMD_CMD1_BIT_POS 2 +#define NFCADDR_CMD_CMD2 (0xff << 10) /* Command for Cycle 2 */ +#define NFCADDR_CMD_CMD2_BIT_POS 10 +#define NFCADDR_CMD_VCMD2 (0x1 << 18) /* Valid Cycle 2 Command */ +#define NFCADDR_CMD_ACYCLE (0x7 << 19) /* Number of Address required */ +#define NFCADDR_CMD_ACYCLE_NONE (0x0 << 19) +#define NFCADDR_CMD_ACYCLE_1 (0x1 << 19) +#define NFCADDR_CMD_ACYCLE_2 (0x2 << 19) +#define NFCADDR_CMD_ACYCLE_3 (0x3 << 19) +#define NFCADDR_CMD_ACYCLE_4 (0x4 << 19) +#define NFCADDR_CMD_ACYCLE_5 (0x5 << 19) +#define NFCADDR_CMD_ACYCLE_BIT_POS 19 +#define NFCADDR_CMD_CSID (0x7 << 22) /* Chip Select Identifier */ +#define NFCADDR_CMD_CSID_0 (0x0 << 22) +#define NFCADDR_CMD_CSID_1 (0x1 << 22) +#define NFCADDR_CMD_CSID_2 (0x2 << 22) +#define NFCADDR_CMD_CSID_3 (0x3 << 22) +#define NFCADDR_CMD_CSID_4 (0x4 << 22) +#define NFCADDR_CMD_CSID_5 (0x5 << 22) +#define NFCADDR_CMD_CSID_6 (0x6 << 22) +#define NFCADDR_CMD_CSID_7 (0x7 << 22) +#define NFCADDR_CMD_DATAEN (0x1 << 25) /* Data Transfer Enable */ +#define NFCADDR_CMD_DATADIS (0x0 << 25) /* Data Transfer Disable */ +#define NFCADDR_CMD_NFCRD (0x0 << 26) /* NFC Read Enable */ +#define NFCADDR_CMD_NFCWR (0x1 << 26) /* NFC Write Enable */ +#define NFCADDR_CMD_NFCBUSY (0x1 << 27) /* NFC Busy */ + +#define nfc_cmd_addr1234_writel(cmd, addr1234, nfc_base) \ + writel((addr1234), (cmd) + nfc_base) + +#define nfc_cmd_readl(bitstatus, nfc_base) \ + readl_relaxed((bitstatus) + nfc_base) + +#define NFC_TIME_OUT_MS 100 +#define NFC_SRAM_BANK1_OFFSET 0x1200 + +#endif -- cgit v0.10.2 From 1ae9c092efcca18876921548b2d16cf7c4b53fda Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Mon, 5 Aug 2013 19:14:36 +0800 Subject: mtd: atmel_nand: enable Nand Flash Controller (NFC) read data via sram NFC has embedded sram which can use to transfer data. This patch enable reading nand flash via NFC SRAM. It will minimize the CPU overhead. This driver has been tested on SAMA5D3X-EK with JFFS2, YAFFS2, UBIFS and mtd-utils. Here puts the part of mtd_speedtest (read test) result as following: Compare with non-NFC mtd_speedtest result, reading will reduce %45 cpu load with increase %80 speed. - commands use to test: # insmod /mnt/mtd_speedtest.ko dev=2 & # top -n 30 -d 1 | grep speedtest - test result: ================================================= mtd_speedtest: MTD device: 2 mtd_speedtest: MTD device size 41943040, eraseblock size 131072, page size 2048, count of eraseblocks 320, pages per eraseblock 64, OOB size 64 mtd_speedtest: testing eraseblock read speed 509 495 root D 1164 0% 28% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 25% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 26% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: eraseblock read speed is 9403 KiB/s mtd_speedtest: testing page read speed 509 495 root R 1164 0% 31% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 57% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 53% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 71% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: page read speed is 9258 KiB/s Signed-off-by: Josh Wu Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 9e0f95d..1905aa6 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -93,9 +93,13 @@ struct atmel_nfc { void __iomem *hsmc_regs; void __iomem *sram_bank0; dma_addr_t sram_bank0_phys; + bool use_nfc_sram; bool is_initialized; struct completion comp_nfc; + + /* Point to the sram bank which include readed data via NFC */ + void __iomem *data_in_sram; }; static struct atmel_nfc nand_nfc; @@ -247,21 +251,43 @@ static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd) return res; } +static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) +{ + int i; + u32 *t = trg; + const __iomem u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + *t++ = readl_relaxed(s++); +} + /* * Minimal-overhead PIO for data access. */ static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; - __raw_readsb(nand_chip->IO_ADDR_R, buf, len); + if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) { + memcpy32_fromio(buf, host->nfc->data_in_sram, len); + host->nfc->data_in_sram += len; + } else { + __raw_readsb(nand_chip->IO_ADDR_R, buf, len); + } } static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; - __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); + if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) { + memcpy32_fromio(buf, host->nfc->data_in_sram, len); + host->nfc->data_in_sram += len; + } else { + __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); + } } static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len) @@ -283,6 +309,40 @@ static void dma_complete_func(void *completion) complete(completion); } +static int nfc_set_sram_bank(struct atmel_nand_host *host, unsigned int bank) +{ + /* NFC only has two banks. Must be 0 or 1 */ + if (bank > 1) + return -EINVAL; + + if (bank) { + /* Only for a 2k-page or lower flash, NFC can handle 2 banks */ + if (host->mtd.writesize > 2048) + return -EINVAL; + nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK1); + } else { + nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK0); + } + + return 0; +} + +static uint nfc_get_sram_off(struct atmel_nand_host *host) +{ + if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1) + return NFC_SRAM_BANK1_OFFSET; + else + return 0; +} + +static dma_addr_t nfc_sram_phys(struct atmel_nand_host *host) +{ + if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1) + return host->nfc->sram_bank0_phys + NFC_SRAM_BANK1_OFFSET; + else + return host->nfc->sram_bank0_phys; +} + static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, int is_read) { @@ -296,6 +356,7 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, void *p = buf; int err = -EIO; enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + struct atmel_nfc *nfc = host->nfc; if (buf >= high_memory) goto err_buf; @@ -312,7 +373,12 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, } if (is_read) { - dma_src_addr = host->io_phys; + if (nfc && nfc->data_in_sram) + dma_src_addr = nfc_sram_phys(host) + (nfc->data_in_sram + - (nfc->sram_bank0 + nfc_get_sram_off(host))); + else + dma_src_addr = host->io_phys; + dma_dst_addr = phys_addr; } else { dma_src_addr = phys_addr; @@ -339,6 +405,10 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, dma_async_issue_pending(host->dma_chan); wait_for_completion(&host->comp); + if (is_read && nfc && nfc->data_in_sram) + /* After read data from SRAM, need to increase the position */ + nfc->data_in_sram += len; + err = 0; err_dma: @@ -850,7 +920,8 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, unsigned long end_time; int bitflips = 0; - pmecc_enable(host, NAND_ECC_READ); + if (!host->nfc || !host->nfc->use_nfc_sram) + pmecc_enable(host, NAND_ECC_READ); chip->read_buf(mtd, buf, eccsize); chip->read_buf(mtd, oob, mtd->oobsize); @@ -1666,6 +1737,7 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, unsigned int addr1234 = 0; unsigned int cycle0 = 0; bool do_addr = true; + host->nfc->data_in_sram = NULL; dev_dbg(host->dev, "%s: cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n", __func__, command, column, page_addr); @@ -1707,6 +1779,16 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, command = NAND_CMD_READ0; /* only READ0 is valid */ cmd1 = command << NFCADDR_CMD_CMD1_BIT_POS; } + if (host->nfc->use_nfc_sram) { + /* Enable Data transfer to sram */ + dataen = NFCADDR_CMD_DATAEN; + + /* Need enable PMECC now, since NFC will transfer + * data in bus after sending nfc read command. + */ + if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) + pmecc_enable(host, NAND_ECC_READ); + } cmd2 = NAND_CMD_READSTART << NFCADDR_CMD_CMD2_BIT_POS; vcmd2 = NFCADDR_CMD_VCMD2; @@ -1728,6 +1810,10 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr; nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0); + if (dataen == NFCADDR_CMD_DATAEN) + if (nfc_wait_interrupt(host, NFC_SR_XFR_DONE)) + dev_err(host->dev, "something wrong, No XFR_DONE interrupt comes.\n"); + /* * Program and erase have their own busy handlers status, sequential * in, and deplete1 need no delay. @@ -1745,12 +1831,66 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, return; case NAND_CMD_READ0: + if (dataen == NFCADDR_CMD_DATAEN) { + host->nfc->data_in_sram = host->nfc->sram_bank0 + + nfc_get_sram_off(host); + return; + } /* fall through */ default: nfc_wait_interrupt(host, NFC_SR_RB_EDGE); } } +static int nfc_sram_init(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + int res = 0; + + /* Initialize the NFC CFG register */ + unsigned int cfg_nfc = 0; + + /* set page size and oob layout */ + switch (mtd->writesize) { + case 512: + cfg_nfc = NFC_CFG_PAGESIZE_512; + break; + case 1024: + cfg_nfc = NFC_CFG_PAGESIZE_1024; + break; + case 2048: + cfg_nfc = NFC_CFG_PAGESIZE_2048; + break; + case 4096: + cfg_nfc = NFC_CFG_PAGESIZE_4096; + break; + case 8192: + cfg_nfc = NFC_CFG_PAGESIZE_8192; + break; + default: + dev_err(host->dev, "Unsupported page size for NFC.\n"); + res = -ENXIO; + return res; + } + + /* oob bytes size = (NFCSPARESIZE + 1) * 4 + * Max support spare size is 512 bytes. */ + cfg_nfc |= (((mtd->oobsize / 4) - 1) << NFC_CFG_NFC_SPARESIZE_BIT_POS + & NFC_CFG_NFC_SPARESIZE); + /* default set a max timeout */ + cfg_nfc |= NFC_CFG_RSPARE | + NFC_CFG_NFC_DTOCYC | NFC_CFG_NFC_DTOMUL; + + nfc_writel(host->nfc->hsmc_regs, CFG, cfg_nfc); + + nfc_set_sram_bank(host, 0); + + dev_info(host->dev, "Using NFC Sram read\n"); + + return 0; +} + static struct platform_driver atmel_nand_nfc_driver; /* * Probe for the NAND device. @@ -1912,6 +2052,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev) goto err_hw_ecc; } + /* initialize the nfc configuration register */ + if (host->nfc && host->nfc->use_nfc_sram) { + res = nfc_sram_init(mtd); + if (res) { + host->nfc->use_nfc_sram = false; + dev_err(host->dev, "Disable use nfc sram for data transfer.\n"); + } + } + /* second phase scan */ if (nand_scan_tail(mtd)) { res = -ENXIO; @@ -1992,11 +2141,13 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev) nfc_sram = platform_get_resource(pdev, IORESOURCE_MEM, 2); if (nfc_sram) { nfc->sram_bank0 = devm_ioremap_resource(&pdev->dev, nfc_sram); - if (IS_ERR(nfc->sram_bank0)) + if (IS_ERR(nfc->sram_bank0)) { dev_warn(&pdev->dev, "Fail to ioremap the NFC sram with error: %ld. So disable NFC sram.\n", PTR_ERR(nfc->sram_bank0)); - else + } else { + nfc->use_nfc_sram = true; nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start; + } } nfc->is_initialized = true; -- cgit v0.10.2 From 6054d4d56307839bffabb687767c24d5ff62563b Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Mon, 5 Aug 2013 19:14:37 +0800 Subject: mtd: atmel_nand: enable Nand Flash Controller (NFC) write via sram This patch enable writing nand flash via NFC SRAM. It will minimize the CPU overhead. The SRAM write only support ECC_NONE and ECC_HW with PMECC. To enable this NFC write by SRAM feature, you can add a string in dts under NFC driver node. This driver has been tested on SAMA5D3X-EK with JFFS2, YAFFS2, UBIFS and mtd-utils. Here is part of mtd_speedtest (writing test) result, compare with non-NFC writing, it reduces %65 cpu load with loss %12 speed. - commands use to test: # insmod /mnt/mtd_speedtest.ko dev=2 & # top -n 30 -d 1 | grep speedtest - test result: ================================================= mtd_speedtest: MTD device: 2 mtd_speedtest: MTD device size 41943040, eraseblock size 131072, page size 2048, count of eraseblocks 320, pages per eraseblock 64, OOB size 64 mtd_speedtest: testing eraseblock write speed 509 495 root D 1164 0% 7% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 8% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root R 1164 0% 5% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: eraseblock write speed is 5194 KiB/s mtd_speedtest: testing page write speed 509 495 root D 1164 0% 32% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 27% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 25% insmod /mnt/mtd_speedtest.ko dev=2 509 495 root D 1164 0% 30% insmod /mnt/mtd_speedtest.ko dev=2 mtd_speedtest: page write speed is 5024 KiB/s Signed-off-by: Josh Wu Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt index 5d8b7d5..c472883 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -36,6 +36,8 @@ Optional properties: - reg : should specify the address and size used for NFC command registers, NFC registers and NFC Sram. NFC Sram address and size can be absent if don't want to use it. + - Optional properties: + - atmel,write-by-sram: boolean to enable NFC write by sram. Examples: nand0: nand@40000000,0 { diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 1905aa6..65b302c 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -94,12 +94,14 @@ struct atmel_nfc { void __iomem *sram_bank0; dma_addr_t sram_bank0_phys; bool use_nfc_sram; + bool write_by_sram; bool is_initialized; struct completion comp_nfc; /* Point to the sram bank which include readed data via NFC */ void __iomem *data_in_sram; + bool will_write_sram; }; static struct atmel_nfc nand_nfc; @@ -261,6 +263,16 @@ static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) *t++ = readl_relaxed(s++); } +static void memcpy32_toio(void __iomem *trg, const void *src, int size) +{ + int i; + u32 __iomem *t = trg; + const u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + writel_relaxed(*s++, t++); +} + /* * Minimal-overhead PIO for data access. */ @@ -382,7 +394,11 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, dma_dst_addr = phys_addr; } else { dma_src_addr = phys_addr; - dma_dst_addr = host->io_phys; + + if (nfc && nfc->write_by_sram) + dma_dst_addr = nfc_sram_phys(host); + else + dma_dst_addr = host->io_phys; } tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr, @@ -954,9 +970,10 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, int i, j; unsigned long end_time; - pmecc_enable(host, NAND_ECC_WRITE); - - chip->write_buf(mtd, (u8 *)buf, mtd->writesize); + if (!host->nfc || !host->nfc->write_by_sram) { + pmecc_enable(host, NAND_ECC_WRITE); + chip->write_buf(mtd, (u8 *)buf, mtd->writesize); + } end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS); while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) { @@ -1798,6 +1815,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, case NAND_CMD_SEQIN: case NAND_CMD_RNDIN: nfcwr = NFCADDR_CMD_NFCWR; + if (host->nfc->will_write_sram && command == NAND_CMD_SEQIN) + dataen = NFCADDR_CMD_DATAEN; break; default: break; @@ -1842,6 +1861,68 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, } } +static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int cached, int raw) +{ + int cfg, len; + int status = 0; + struct atmel_nand_host *host = chip->priv; + void __iomem *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host); + + /* Subpage write is not supported */ + if (offset || (data_len < mtd->writesize)) + return -EINVAL; + + cfg = nfc_readl(host->nfc->hsmc_regs, CFG); + len = mtd->writesize; + + if (unlikely(raw)) { + len += mtd->oobsize; + nfc_writel(host->nfc->hsmc_regs, CFG, cfg | NFC_CFG_WSPARE); + } else + nfc_writel(host->nfc->hsmc_regs, CFG, cfg & ~NFC_CFG_WSPARE); + + /* Copy page data to sram that will write to nand via NFC */ + if (use_dma) { + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) != 0) + /* Fall back to use cpu copy */ + memcpy32_toio(sram, buf, len); + } else { + memcpy32_toio(sram, buf, len); + } + + if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) + /* + * When use NFC sram, need set up PMECC before send + * NAND_CMD_SEQIN command. Since when the nand command + * is sent, nfc will do transfer from sram and nand. + */ + pmecc_enable(host, NAND_ECC_WRITE); + + host->nfc->will_write_sram = true; + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + host->nfc->will_write_sram = false; + + if (likely(!raw)) + /* Need to write ecc into oob */ + status = chip->ecc.write_page(mtd, chip, buf, oob_required); + + if (status < 0) + return status; + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + static int nfc_sram_init(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; @@ -1884,10 +1965,20 @@ static int nfc_sram_init(struct mtd_info *mtd) nfc_writel(host->nfc->hsmc_regs, CFG, cfg_nfc); + host->nfc->will_write_sram = false; nfc_set_sram_bank(host, 0); - dev_info(host->dev, "Using NFC Sram read\n"); + /* Use Write page with NFC SRAM only for PMECC or ECC NONE. */ + if (host->nfc->write_by_sram) { + if ((chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) || + chip->ecc.mode == NAND_ECC_NONE) + chip->write_page = nfc_sram_write_page; + else + host->nfc->write_by_sram = false; + } + dev_info(host->dev, "Using NFC Sram read %s\n", + host->nfc->write_by_sram ? "and write" : ""); return 0; } @@ -2147,6 +2238,11 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev) } else { nfc->use_nfc_sram = true; nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start; + + if (pdev->dev.of_node) + nfc->write_by_sram = of_property_read_bool( + pdev->dev.of_node, + "atmel,write-by-sram"); } } -- cgit v0.10.2 From e79265ba6bdb31437bd4c3e7911950f9d1262a07 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Mon, 5 Aug 2013 19:14:38 +0800 Subject: mtd: ofpart: add compatible check for child nodes In case that the nand device will support some features like Nand Flash Controller, we want to make the sub feature as a sub node of nand device. Use such organization it is easy to enable/disable feature, also it is back compatible and more readable. If the sub-node has a compatible property then it is a driver not partition. Signed-off-by: Josh Wu Acked-by: Brian Norris Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Artem Bityutskiy [ added a missing newline -Brian ] Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/Documentation/devicetree/bindings/mtd/partition.txt b/Documentation/devicetree/bindings/mtd/partition.txt index 9315ac9..8e5557d 100644 --- a/Documentation/devicetree/bindings/mtd/partition.txt +++ b/Documentation/devicetree/bindings/mtd/partition.txt @@ -4,6 +4,7 @@ Partitions can be represented by sub-nodes of an mtd device. This can be used on platforms which have strong conventions about which portions of a flash are used for what purposes, but which don't use an on-flash partition table such as RedBoot. +NOTE: if the sub-node has a compatible string, then it is not a partition. #address-cells & #size-cells must both be present in the mtd device. There are two valid values for both: diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 553d6d6..7843a44 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -20,6 +20,11 @@ #include #include +static bool node_has_compatible(struct device_node *pp) +{ + return of_get_property(pp, "compatible", NULL); +} + static int parse_ofpart_partitions(struct mtd_info *master, struct mtd_partition **pparts, struct mtd_part_parser_data *data) @@ -40,8 +45,12 @@ static int parse_ofpart_partitions(struct mtd_info *master, /* First count the subnodes */ pp = NULL; nr_parts = 0; - while ((pp = of_get_next_child(node, pp))) + while ((pp = of_get_next_child(node, pp))) { + if (node_has_compatible(pp)) + continue; + nr_parts++; + } if (nr_parts == 0) return 0; @@ -57,6 +66,9 @@ static int parse_ofpart_partitions(struct mtd_info *master, int len; int a_cells, s_cells; + if (node_has_compatible(pp)) + continue; + reg = of_get_property(pp, "reg", &len); if (!reg) { nr_parts--; -- cgit v0.10.2 From 771c568bcf915e708ae819ef9d07d862f7e2da86 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 30 Jul 2013 17:52:55 -0700 Subject: mtd: nand: add accessors, macros for in-memory BBT There is an abundance of magic numbers and complicated shifting/masking logic in the in-memory BBT code which makes the code unnecessary complex and hard to read. This patch adds macros to represent the 00b, 01b, 10b, and 11b memory-BBT magic numbers, as well as two accessor functions for reading and marking the memory-BBT bitfield for a given block. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 2672643..4c57a9b 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -71,6 +71,28 @@ #include #include +#define BBT_BLOCK_GOOD 0x00 +#define BBT_BLOCK_WORN 0x01 +#define BBT_BLOCK_RESERVED 0x02 +#define BBT_BLOCK_FACTORY_BAD 0x03 + +#define BBT_ENTRY_MASK 0x03 +#define BBT_ENTRY_SHIFT 2 + +static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) +{ + uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; + entry >>= (block & BBT_ENTRY_MASK) * 2; + return entry & BBT_ENTRY_MASK; +} + +static inline void bbt_mark_entry(struct nand_chip *chip, int block, + uint8_t mark) +{ + uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); + chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; +} + static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) { if (memcmp(buf, td->pattern, td->len)) @@ -216,7 +238,9 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, if (reserved_block_code && (tmp == reserved_block_code)) { pr_info("nand_read_bbt: reserved block at 0x%012llx\n", (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); - this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + bbt_mark_entry(this, (offs << 2) + + (act >> 1), + BBT_BLOCK_RESERVED); mtd->ecc_stats.bbtblocks++; continue; } @@ -228,9 +252,13 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); /* Factory marked bad or worn out? */ if (tmp == 0) - this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + bbt_mark_entry(this, (offs << 2) + + (act >> 1), + BBT_BLOCK_FACTORY_BAD); else - this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + bbt_mark_entry(this, (offs << 2) + + (act >> 1), + BBT_BLOCK_WORN); mtd->ecc_stats.badblocks++; } } @@ -526,7 +554,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, return ret; if (ret) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); + bbt_mark_entry(this, i >> 1, BBT_BLOCK_FACTORY_BAD); pr_warn("Bad eraseblock %d at 0x%012llx\n", i >> 1, (unsigned long long)from); mtd->ecc_stats.badblocks++; @@ -713,10 +741,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, for (i = 0; i < td->maxblocks; i++) { int block = startblock + dir * i; /* Check, if the block is bad */ - switch ((this->bbt[block >> 2] >> - (2 * (block & 0x03))) & 0x03) { - case 0x01: - case 0x03: + switch (bbt_get_entry(this, block)) { + case BBT_BLOCK_WORN: + case BBT_BLOCK_FACTORY_BAD: continue; } page = block << @@ -816,7 +843,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, /* Walk through the memory table */ for (i = 0; i < numblocks;) { uint8_t dat; - dat = this->bbt[bbtoffs + (i >> 2)]; + dat = bbt_get_entry(this, (bbtoffs << 2) + i); for (j = 0; j < 4; j++, i++) { int sftcnt = (i << (3 - sft)) & sftmsk; /* Do not store the reserved bbt blocks! */ @@ -1009,7 +1036,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) { struct nand_chip *this = mtd->priv; int i, j, chips, block, nrblocks, update; - uint8_t oldval, newval; + uint8_t oldval; /* Do we have a bbt per chip? */ if (td->options & NAND_BBT_PERCHIP) { @@ -1027,10 +1054,10 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) continue; block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); block <<= 1; - oldval = this->bbt[(block >> 3)]; - newval = oldval | (0x2 << (block & 0x06)); - this->bbt[(block >> 3)] = newval; - if ((oldval != newval) && td->reserved_block_code) + oldval = bbt_get_entry(this, block >> 1); + bbt_mark_entry(this, block >> 1, BBT_BLOCK_RESERVED); + if ((oldval != BBT_BLOCK_RESERVED) && + td->reserved_block_code) nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); continue; } @@ -1041,10 +1068,9 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) block = i * nrblocks; block <<= 1; for (j = 0; j < td->maxblocks; j++) { - oldval = this->bbt[(block >> 3)]; - newval = oldval | (0x2 << (block & 0x06)); - this->bbt[(block >> 3)] = newval; - if (oldval != newval) + oldval = bbt_get_entry(this, block >> 1); + bbt_mark_entry(this, block >> 1, BBT_BLOCK_RESERVED); + if (oldval != BBT_BLOCK_RESERVED) update = 1; block += 2; } @@ -1361,18 +1387,18 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) /* Get block number * 2 */ block = (int)(offs >> (this->bbt_erase_shift - 1)); - res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + res = bbt_get_entry(this, block >> 1); pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: " "(block %d) 0x%02x\n", (unsigned int)offs, block >> 1, res); switch ((int)res) { - case 0x00: + case BBT_BLOCK_GOOD: return 0; - case 0x01: + case BBT_BLOCK_WORN: return 1; - case 0x02: + case BBT_BLOCK_RESERVED: return allowbbt ? 0 : 1; } return 1; -- cgit v0.10.2 From b4d20d601f1e2bbab46d25217303ccefe11e263e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 30 Jul 2013 17:52:56 -0700 Subject: mtd: nand: remove multiplied-by-2 block logic The parent commit 771c568bcf915e708ae819ef9d07d862f7e2da86 ("mtd: nand: add accessors, macros for in-memory BBT") makes the following comment obsolete: /* * Note that numblocks is 2 * (real numblocks) here, see i+=2 * below as it makes shifting and masking less painful */ I don't think it ever could have been "less painful" to have to shift an extra bit (or 2, or 3) at various points in nand_bbt.c (and even outside, since we leak our in-memory format). But now it is certainly more painful, since we have nice macros and functions to retrieve the relevant portions of the BBT. This patch removes any points where the block number is doubled/halved/otherwise-shifted, instead representing the block number in its most natural form: as the actual block number. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 4c57a9b..3ff9d36 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -181,7 +181,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td) * @page: the starting page * @num: the number of bbt descriptors to read * @td: the bbt describtion table - * @offs: offset in the memory table + * @offs: block number offset in the table * * Read the bad block table starting from page. */ @@ -231,15 +231,15 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, /* Analyse data */ for (i = 0; i < len; i++) { uint8_t dat = buf[i]; - for (j = 0; j < 8; j += bits, act += 2) { + for (j = 0; j < 8; j += bits, act++) { uint8_t tmp = (dat >> j) & msk; if (tmp == msk) continue; if (reserved_block_code && (tmp == reserved_block_code)) { pr_info("nand_read_bbt: reserved block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); - bbt_mark_entry(this, (offs << 2) + - (act >> 1), + (loff_t)(offs + act) << + this->bbt_erase_shift); + bbt_mark_entry(this, offs + act, BBT_BLOCK_RESERVED); mtd->ecc_stats.bbtblocks++; continue; @@ -249,15 +249,14 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, * move this message to pr_debug. */ pr_info("nand_read_bbt: bad block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + (loff_t)(offs + act) << + this->bbt_erase_shift); /* Factory marked bad or worn out? */ if (tmp == 0) - bbt_mark_entry(this, (offs << 2) + - (act >> 1), + bbt_mark_entry(this, offs + act, BBT_BLOCK_FACTORY_BAD); else - bbt_mark_entry(this, (offs << 2) + - (act >> 1), + bbt_mark_entry(this, offs + act, BBT_BLOCK_WORN); mtd->ecc_stats.badblocks++; } @@ -293,7 +292,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc td, offs); if (res) return res; - offs += this->chipsize >> (this->bbt_erase_shift + 2); + offs += this->chipsize >> this->bbt_erase_shift; } } else { res = read_bbt(mtd, buf, td->pages[0], @@ -517,11 +516,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, } if (chip == -1) { - /* - * Note that numblocks is 2 * (real numblocks) here, see i+=2 - * below as it makes shifting and masking less painful - */ - numblocks = mtd->size >> (this->bbt_erase_shift - 1); + numblocks = mtd->size >> this->bbt_erase_shift; startblock = 0; from = 0; } else { @@ -530,16 +525,16 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, chip + 1, this->numchips); return -EINVAL; } - numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + numblocks = this->chipsize >> this->bbt_erase_shift; startblock = chip * numblocks; numblocks += startblock; - from = (loff_t)startblock << (this->bbt_erase_shift - 1); + from = (loff_t)startblock << this->bbt_erase_shift; } if (this->bbt_options & NAND_BBT_SCANLASTPAGE) from += mtd->erasesize - (mtd->writesize * numpages); - for (i = startblock; i < numblocks;) { + for (i = startblock; i < numblocks; i++) { int ret; BUG_ON(bd->options & NAND_BBT_NO_OOB); @@ -554,13 +549,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, return ret; if (ret) { - bbt_mark_entry(this, i >> 1, BBT_BLOCK_FACTORY_BAD); + bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); pr_warn("Bad eraseblock %d at 0x%012llx\n", - i >> 1, (unsigned long long)from); + i, (unsigned long long)from); mtd->ecc_stats.badblocks++; } - i += 2; from += (1 << this->bbt_erase_shift); } return 0; @@ -683,9 +677,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, { struct nand_chip *this = mtd->priv; struct erase_info einfo; - int i, j, res, chip = 0; + int i, res, chip = 0; int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; - int nrchips, bbtoffs, pageoffs, ooboffs; + int nrchips, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; size_t retlen, len = 0; @@ -775,8 +769,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, default: return -EINVAL; } - bbtoffs = chip * (numblocks >> 2); - to = ((loff_t)page) << this->page_shift; /* Must we save the block contents? */ @@ -841,16 +833,12 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, buf[ooboffs + td->veroffs] = td->version[chip]; /* Walk through the memory table */ - for (i = 0; i < numblocks;) { + for (i = 0; i < numblocks; i++) { uint8_t dat; - dat = bbt_get_entry(this, (bbtoffs << 2) + i); - for (j = 0; j < 4; j++, i++) { - int sftcnt = (i << (3 - sft)) & sftmsk; - /* Do not store the reserved bbt blocks! */ - buf[offs + (i >> sft)] &= - ~(msk[dat & 0x03] << sftcnt); - dat >>= 2; - } + int sftcnt = (i << (3 - sft)) & sftmsk; + dat = bbt_get_entry(this, chip * numblocks + i); + /* Do not store the reserved bbt blocks! */ + buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); } memset(&einfo, 0, sizeof(einfo)); @@ -1053,12 +1041,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) if (td->pages[i] == -1) continue; block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); - block <<= 1; - oldval = bbt_get_entry(this, block >> 1); - bbt_mark_entry(this, block >> 1, BBT_BLOCK_RESERVED); + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); if ((oldval != BBT_BLOCK_RESERVED) && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)block << + this->bbt_erase_shift); continue; } update = 0; @@ -1066,13 +1054,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) block = ((i + 1) * nrblocks) - td->maxblocks; else block = i * nrblocks; - block <<= 1; for (j = 0; j < td->maxblocks; j++) { - oldval = bbt_get_entry(this, block >> 1); - bbt_mark_entry(this, block >> 1, BBT_BLOCK_RESERVED); + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); if (oldval != BBT_BLOCK_RESERVED) update = 1; - block += 2; + block++; } /* * If we want reserved blocks to be recorded to flash, and some @@ -1080,7 +1067,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) * bbts. This should only happen once. */ if (update && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)(block - 1) << + this->bbt_erase_shift); } } @@ -1385,13 +1373,12 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) int block; uint8_t res; - /* Get block number * 2 */ - block = (int)(offs >> (this->bbt_erase_shift - 1)); - res = bbt_get_entry(this, block >> 1); + block = (int)(offs >> this->bbt_erase_shift); + res = bbt_get_entry(this, block); pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: " "(block %d) 0x%02x\n", - (unsigned int)offs, block >> 1, res); + (unsigned int)offs, block, res); switch ((int)res) { case BBT_BLOCK_GOOD: -- cgit v0.10.2 From 39dbb02998d859f0fa12c5b495fe90681ba45ce2 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 30 Jul 2013 17:52:57 -0700 Subject: mtd: nand: eliminate cast Just make 'res' an int. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 3ff9d36..3f18776 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1370,8 +1370,7 @@ int nand_default_bbt(struct mtd_info *mtd) int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) { struct nand_chip *this = mtd->priv; - int block; - uint8_t res; + int block, res; block = (int)(offs >> this->bbt_erase_shift); res = bbt_get_entry(this, block); @@ -1380,7 +1379,7 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) "(block %d) 0x%02x\n", (unsigned int)offs, block, res); - switch ((int)res) { + switch (res) { case BBT_BLOCK_GOOD: return 0; case BBT_BLOCK_WORN: -- cgit v0.10.2 From 5a0edb251ae91c6f9b1f28dc165becd955666118 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 30 Jul 2013 17:52:58 -0700 Subject: mtd: nand: refactor chip->block_markbad interface The chip->block_markbad pointer should really only be responsible for writing a bad block marker for new bad blocks. It should not take care of BBT-related functionality, nor should it handle bookkeeping of bad block stats. This patch refactors the 3 users of the block_markbad interface (plus the default nand_base implementation) so that the common code is kept in nand_block_markbad_lowlevel(). It removes some inconsistencies between the various implementations and should allow for more centralized improvements in the future. Because gpmi-nand no longer needs the nand_update_bbt() function, let's stop exporting it as well. Signed-off-by: Brian Norris Acked-by: Huang Shijie (for gpmi-nand parts) Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index dc86d4a..548db23 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -1093,7 +1093,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) 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); @@ -1108,9 +1107,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) 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++) @@ -1120,8 +1116,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) write_page_prologue(mtd, g4_addr); docg4_write_page(mtd, nand, buf, 1); ret = pageprog(mtd); - if (!ret) - mtd->ecc_stats.badblocks++; kfree(buf); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index ef0978e..bcf9bc5 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1148,43 +1148,31 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; struct gpmi_nand_data *this = chip->priv; - int block, ret = 0; + int ret = 0; uint8_t *block_mark; int column, page, status, chipnr; - /* Get block number */ - block = (int)(ofs >> chip->bbt_erase_shift); - if (chip->bbt) - chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + chipnr = (int)(ofs >> chip->chip_shift); + chip->select_chip(mtd, chipnr); - /* Do we have a flash based bad block table ? */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) - ret = nand_update_bbt(mtd, ofs); - else { - chipnr = (int)(ofs >> chip->chip_shift); - chip->select_chip(mtd, chipnr); + column = this->swap_block_mark ? mtd->writesize : 0; - column = this->swap_block_mark ? mtd->writesize : 0; + /* Write the block mark. */ + block_mark = this->data_buffer_dma; + block_mark[0] = 0; /* bad block marker */ - /* Write the block mark. */ - block_mark = this->data_buffer_dma; - block_mark[0] = 0; /* bad block marker */ + /* Shift to get page */ + page = (int)(ofs >> chip->page_shift); - /* Shift to get page */ - page = (int)(ofs >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page); + chip->write_buf(mtd, block_mark, 1); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page); - chip->write_buf(mtd, block_mark, 1); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + ret = -EIO; - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - ret = -EIO; - - chip->select_chip(mtd, -1); - } - if (!ret) - mtd->ecc_stats.badblocks++; + chip->select_chip(mtd, -1); return ret; } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 158240d..5a7467c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -324,13 +324,58 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) } /** - * nand_default_block_markbad - [DEFAULT] mark a block bad + * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker * @mtd: MTD device structure * @ofs: offset from device start * * This is the default implementation, which can be overridden by a hardware - * specific driver. We try operations in the following order, according to our - * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + * specific driver. It provides the details for writing a bad block marker to a + * block. + */ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; + uint8_t buf[2] = { 0, 0 }; + int ret = 0, res, i = 0; + + ops.datbuf = NULL; + ops.oobbuf = buf; + 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) + ofs += mtd->erasesize - mtd->writesize; + do { + res = nand_do_write_oob(mtd, ofs, &ops); + if (!ret) + ret = res; + + i++; + ofs += mtd->writesize; + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); + + return ret; +} + +/** + * nand_block_markbad_lowlevel - mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This function performs the generic NAND bad block marking steps (i.e., bad + * block table(s) and/or marker(s)). We only allow the hardware driver to + * specify how to write bad block markers to OOB (chip->block_markbad). + * + * 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 @@ -338,11 +383,10 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) * 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) +static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; - uint8_t buf[2] = { 0, 0 }; - int block, res, ret = 0, i = 0; + int block, res, ret = 0; int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); if (write_oob) { @@ -364,34 +408,8 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* Write bad block marker to OOB */ if (write_oob) { - struct mtd_oob_ops ops; - loff_t wr_ofs = ofs; - nand_get_device(mtd, FL_WRITING); - - ops.datbuf = NULL; - ops.oobbuf = buf; - 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 { - res = nand_do_write_oob(mtd, wr_ofs, &ops); - if (!ret) - ret = res; - - i++; - wr_ofs += mtd->writesize; - } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); - + ret = chip->block_markbad(mtd, ofs); nand_release_device(mtd); } @@ -2683,7 +2701,6 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) */ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct nand_chip *chip = mtd->priv; int ret; ret = nand_block_isbad(mtd, ofs); @@ -2694,7 +2711,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) return ret; } - return chip->block_markbad(mtd, ofs); + return nand_block_markbad_lowlevel(mtd, ofs); } /** diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 3f18776..bac481a 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1392,4 +1392,3 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) EXPORT_SYMBOL(nand_scan_bbt); EXPORT_SYMBOL(nand_default_bbt); -EXPORT_SYMBOL_GPL(nand_update_bbt); diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index e8181ed..e06b5e5 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -42,7 +42,7 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_oob_ops ops; struct sm_oob oob; - int ret, error = 0; + int ret; memset(&oob, -1, SM_OOB_SIZE); oob.block_status = 0x0F; @@ -61,11 +61,10 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) printk(KERN_NOTICE "sm_common: can't mark sector at %i as bad\n", (int)ofs); - error = -EIO; - } else - mtd->ecc_stats.badblocks++; + return -EIO; + } - return error; + return 0; } static struct nand_flash_dev nand_smartmedia_flash_ids[] = { -- cgit v0.10.2 From b32843b772db6024336e36c39359d8edc3b416ab Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 30 Jul 2013 17:52:59 -0700 Subject: mtd: nand: hide in-memory BBT implementation details nand_base.c shouldn't have to know the implementation details of nand_bbt's in-memory BBT. Specifically, nand_base shouldn't perform the bit masking and shifting to isolate a BBT entry. Instead, just move some of the BBT code into a new nand_markbad_bbt() interface. This interface allows external users (i.e., nand_base) to mark a single block as bad in the BBT. Then nand_bbt will take care of modifying the in-memory BBT and updating the flash-based BBT (if applicable). Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5a7467c..9a48758 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -374,22 +374,20 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) * block table(s) and/or marker(s)). We only allow the hardware driver to * specify how to write bad block markers to OOB (chip->block_markbad). * - * We try operations in the following order, according to our bbt_options - * (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + * We try operations in the following order: * (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 + * (2) write bad block marker to OOB area of affected block (unless flag + * NAND_BBT_NO_OOB_BBM is present) + * (3) update the BBT + * Note that we retain the first error encountered in (2) or (3), finish the * procedures, and dump the error in the end. */ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; - int block, res, ret = 0; - int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); + int res, ret = 0; - if (write_oob) { + if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { struct erase_info einfo; /* Attempt erase before marking OOB */ @@ -398,24 +396,16 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) 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); - /* Write bad block marker to OOB */ - if (write_oob) { + /* Write bad block marker to OOB */ nand_get_device(mtd, FL_WRITING); ret = chip->block_markbad(mtd, ofs); nand_release_device(mtd); } - /* Update flash-based bad block table */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) { - res = nand_update_bbt(mtd, ofs); + /* Mark block bad in BBT */ + if (chip->bbt) { + res = nand_markbad_bbt(mtd, ofs); if (!ret) ret = res; } diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index bac481a..ae3fb58 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -79,6 +79,8 @@ #define BBT_ENTRY_MASK 0x03 #define BBT_ENTRY_SHIFT 2 +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); + static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) { uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; @@ -1194,13 +1196,13 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) } /** - * nand_update_bbt - [NAND Interface] update bad block table(s) + * nand_update_bbt - update bad block table(s) * @mtd: MTD device structure * @offs: the offset of the newly marked block * * The function updates the bad block table(s). */ -int nand_update_bbt(struct mtd_info *mtd, loff_t offs) +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) { struct nand_chip *this = mtd->priv; int len, res = 0; @@ -1390,5 +1392,27 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) return 1; } +/** + * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT + * @mtd: MTD device structure + * @offs: offset of the bad block + */ +int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int block, ret = 0; + + block = (int)(offs >> this->bbt_erase_shift); + + /* Mark bad block in memory */ + bbt_mark_entry(this, block, BBT_BLOCK_WORN); + + /* Update flash-based bad block table */ + if (this->bbt_options & NAND_BBT_USE_FLASH) + ret = nand_update_bbt(mtd, offs); + + return ret; +} + EXPORT_SYMBOL(nand_scan_bbt); EXPORT_SYMBOL(nand_default_bbt); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0745a42..9f7b248 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -628,8 +628,8 @@ extern struct nand_flash_dev nand_flash_ids[]; extern struct nand_manufacturers nand_manuf_ids[]; extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); -extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_default_bbt(struct mtd_info *mtd); +extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt); -- cgit v0.10.2 From dad2256269cb2ee3a72baefc5eb6e02ae1de2cfe Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 30 Jul 2013 17:53:00 -0700 Subject: mtd: nand: remove NAND_BBT_SCANEMPTY NAND_BBT_SCANEMPTY is a strange, badly-supported option with omap as its single remaining user. NAND_BBT_SCANEMPTY was likely used by accident in omap2[1]. And anyway, omap2 doesn't scan the chip for bad blocks (courtesy of NAND_SKIP_BBTSCAN), and so its use of this option is irrelevant. This patch drops the NAND_BBT_SCANEMPTY option. [1] http://lists.infradead.org/pipermail/linux-mtd/2012-July/042902.html Signed-off-by: Brian Norris Cc: Ivan Djelic Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl index fe122d6..a248f42 100644 --- a/Documentation/DocBook/mtdnand.tmpl +++ b/Documentation/DocBook/mtdnand.tmpl @@ -1224,8 +1224,6 @@ in this page #define NAND_BBT_CREATE 0x00000200 /* Search good / bad pattern through all pages of a block */ #define NAND_BBT_SCANALLPAGES 0x00000400 -/* Scan block empty during good / bad block scan */ -#define NAND_BBT_SCANEMPTY 0x00000800 /* Write bbt if neccecary */ #define NAND_BBT_WRITE 0x00001000 /* Read and write back block contents when writing bbt */ diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index ae3fb58..bc06196 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -110,33 +110,17 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) * @td: search pattern descriptor * * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if - * all bytes except the pattern area contain 0xff. + * good / bad block identifiers. */ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int end = 0; - uint8_t *p = buf; - if (td->options & NAND_BBT_NO_OOB) return check_pattern_no_oob(buf, td); - end = paglen + td->offs; - if (td->options & NAND_BBT_SCANEMPTY) - if (memchr_inv(p, 0xff, end)) - return -1; - p += end; - /* Compare the pattern */ - if (memcmp(p, td->pattern, td->len)) + if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) return -1; - if (td->options & NAND_BBT_SCANEMPTY) { - p += td->len; - end += td->len; - if (memchr_inv(p, 0xff, len - end)) - return -1; - } return 0; } @@ -507,15 +491,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, else numpages = 1; - if (!(bd->options & NAND_BBT_SCANEMPTY)) { - /* We need only read few bytes from the OOB area */ - scanlen = 0; - readlen = bd->len; - } else { - /* Full page content should be read */ - scanlen = mtd->writesize + mtd->oobsize; - readlen = numpages * mtd->writesize; - } + /* We need only read few bytes from the OOB area */ + scanlen = 0; + readlen = bd->len; if (chip == -1) { numblocks = mtd->size >> this->bbt_erase_shift; @@ -882,7 +860,6 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b { struct nand_chip *this = mtd->priv; - bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt(mtd, this->buffers->databuf, bd, -1); } diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index daa3dfc..cb40f87 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -154,7 +154,7 @@ static struct nand_ecclayout omap_oobinfo; */ static uint8_t scan_ff_pattern[] = { 0xff }; static struct nand_bbt_descr bb_descrip_flashbased = { - .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .options = NAND_BBT_SCANALLPAGES, .offs = 0, .len = 1, .pattern = scan_ff_pattern, diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 66fe3b7..08d0085 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -133,7 +133,6 @@ static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_desc { struct onenand_chip *this = mtd->priv; - bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt(mtd, this->page_buf, bd, -1); } diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 211ff67..95fc482 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -93,8 +93,6 @@ struct nand_bbt_descr { #define NAND_BBT_CREATE_EMPTY 0x00000400 /* Search good / bad pattern through all pages of a block */ #define NAND_BBT_SCANALLPAGES 0x00000800 -/* Scan block empty during good / bad block scan */ -#define NAND_BBT_SCANEMPTY 0x00001000 /* Write bbt if neccecary */ #define NAND_BBT_WRITE 0x00002000 /* Read and write back block contents when writing bbt */ -- cgit v0.10.2 From 084db4b020c7bb7f4169ae74fb9a685c30a2939d Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:07 +0900 Subject: mtd: tests: introduce helper functions This introduces the helper functions which can be used by several mtd/tests modules. The following three functions are used all over the test modules. - mtdtest_erase_eraseblock() - mtdtest_scan_for_bad_eraseblocks() - mtdtest_erase_good_eraseblocks() The following are wrapper functions for mtd_read() and mtd_write() which can simplify the return value check. - mtdtest_read() - mtdtest_write() All helpers are put into a single .c file and it will be linked to every test module later. The code will actually be copied to every test module, but it is fine for our small test infrastructure. [dwmw2: merge later 'return -EIO when mtdtest_read() failed' fix] Signed-off-by: Akinobu Mita Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c new file mode 100644 index 0000000..bda8c4d --- /dev/null +++ b/drivers/mtd/tests/mtd_test.c @@ -0,0 +1,110 @@ +#define pr_fmt(fmt) "mtd_test: " fmt + +#include +#include +#include +#include + +#include "mtd_test.h" + +int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd_erase(mtd, &ei); + if (err) { + pr_info("error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + pr_info("some erase error occurred at EB %d\n", ebnum); + return -EIO; + } + return 0; +} + +static int is_block_bad(struct mtd_info *mtd, unsigned int ebnum) +{ + int ret; + loff_t addr = ebnum * mtd->erasesize; + + ret = mtd_block_isbad(mtd, addr); + if (ret) + pr_info("block %d is bad\n", ebnum); + + return ret; +} + +int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, + unsigned int eb, int ebcnt) +{ + int i, bad = 0; + + if (!mtd_can_have_bb(mtd)) + return 0; + + pr_info("scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(mtd, eb + i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); + + return 0; +} + +int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, + unsigned int eb, int ebcnt) +{ + int err; + unsigned int i; + + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = mtdtest_erase_eraseblock(mtd, eb + i); + if (err) + return err; + cond_resched(); + } + + return 0; +} + +int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf) +{ + size_t read; + int err; + + err = mtd_read(mtd, addr, size, &read, buf); + /* Ignore corrected ECC errors */ + if (mtd_is_bitflip(err)) + err = 0; + if (!err && read != size) + err = -EIO; + + return err; +} + +int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size, + const void *buf) +{ + size_t written; + int err; + + err = mtd_write(mtd, addr, size, &written, buf); + if (!err && written != size) + err = -EIO; + + return err; +} diff --git a/drivers/mtd/tests/mtd_test.h b/drivers/mtd/tests/mtd_test.h new file mode 100644 index 0000000..f437c77 --- /dev/null +++ b/drivers/mtd/tests/mtd_test.h @@ -0,0 +1,11 @@ +#include + +int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum); +int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, + unsigned int eb, int ebcnt); +int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, + unsigned int eb, int ebcnt); + +int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf); +int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size, + const void *buf); -- cgit v0.10.2 From a995c792280db558f994c253c1c3c3a55977d529 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:08 +0900 Subject: mtd: tests: rename sources in order to link a helper object Each mtd test module have a single source whose name is the same as the module name. In order to link a single object including helper functions to every test module, this rename these sources to the different names. Signed-off-by: Akinobu Mita Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/Makefile b/drivers/mtd/tests/Makefile index bd0065c..937a829 100644 --- a/drivers/mtd/tests/Makefile +++ b/drivers/mtd/tests/Makefile @@ -7,3 +7,12 @@ obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o obj-$(CONFIG_MTD_TESTS) += mtd_nandbiterrs.o + +mtd_oobtest-objs := oobtest.o mtd_test.o +mtd_pagetest-objs := pagetest.o mtd_test.o +mtd_readtest-objs := readtest.o mtd_test.o +mtd_speedtest-objs := speedtest.o mtd_test.o +mtd_stresstest-objs := stresstest.o mtd_test.o +mtd_subpagetest-objs := subpagetest.o mtd_test.o +mtd_torturetest-objs := torturetest.o mtd_test.o +mtd_nandbiterrs-objs := nandbiterrs.o mtd_test.o diff --git a/drivers/mtd/tests/mtd_nandbiterrs.c b/drivers/mtd/tests/mtd_nandbiterrs.c deleted file mode 100644 index 207bf9a..0000000 --- a/drivers/mtd/tests/mtd_nandbiterrs.c +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright © 2012 NetCommWireless - * Iwo Mergler - * - * Test for multi-bit error recovery on a NAND page This mostly tests the - * ECC controller / driver. - * - * There are two test modes: - * - * 0 - artificially inserting bit errors until the ECC fails - * This is the default method and fairly quick. It should - * be independent of the quality of the FLASH. - * - * 1 - re-writing the same pattern repeatedly until the ECC fails. - * This method relies on the physics of NAND FLASH to eventually - * generate '0' bits if '1' has been written sufficient times. - * Depending on the NAND, the first bit errors will appear after - * 1000 or more writes and then will usually snowball, reaching the - * limits of the ECC quickly. - * - * The test stops after 10000 cycles, should your FLASH be - * exceptionally good and not generate bit errors before that. Try - * a different page in that case. - * - * Please note that neither of these tests will significantly 'use up' any - * FLASH endurance. Only a maximum of two erase operations will be performed. - * - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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; see the file COPYING. If not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -static int dev; -module_param(dev, int, S_IRUGO); -MODULE_PARM_DESC(dev, "MTD device number to use"); - -static unsigned page_offset; -module_param(page_offset, uint, S_IRUGO); -MODULE_PARM_DESC(page_offset, "Page number relative to dev start"); - -static unsigned seed; -module_param(seed, uint, S_IRUGO); -MODULE_PARM_DESC(seed, "Random seed"); - -static int mode; -module_param(mode, int, S_IRUGO); -MODULE_PARM_DESC(mode, "0=incremental errors, 1=overwrite test"); - -static unsigned max_overwrite = 10000; - -static loff_t offset; /* Offset of the page we're using. */ -static unsigned eraseblock; /* Eraseblock number for our page. */ - -/* We assume that the ECC can correct up to a certain number - * of biterrors per subpage. */ -static unsigned subsize; /* Size of subpages */ -static unsigned subcount; /* Number of subpages per page */ - -static struct mtd_info *mtd; /* MTD device */ - -static uint8_t *wbuffer; /* One page write / compare buffer */ -static uint8_t *rbuffer; /* One page read buffer */ - -/* 'random' bytes from known offsets */ -static uint8_t hash(unsigned offset) -{ - unsigned v = offset; - unsigned char c; - v ^= 0x7f7edfd3; - v = v ^ (v >> 3); - v = v ^ (v >> 5); - v = v ^ (v >> 13); - c = v & 0xFF; - /* Reverse bits of result. */ - c = (c & 0x0F) << 4 | (c & 0xF0) >> 4; - c = (c & 0x33) << 2 | (c & 0xCC) >> 2; - c = (c & 0x55) << 1 | (c & 0xAA) >> 1; - return c; -} - -static int erase_block(void) -{ - int err; - struct erase_info ei; - loff_t addr = eraseblock * mtd->erasesize; - - pr_info("erase_block\n"); - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err || ei.state == MTD_ERASE_FAILED) { - pr_err("error %d while erasing\n", err); - if (!err) - err = -EIO; - return err; - } - - return 0; -} - -/* Writes wbuffer to page */ -static int write_page(int log) -{ - int err = 0; - size_t written; - - if (log) - pr_info("write_page\n"); - - err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer); - if (err || written != mtd->writesize) { - pr_err("error: write failed at %#llx\n", (long long)offset); - if (!err) - err = -EIO; - } - - return err; -} - -/* Re-writes the data area while leaving the OOB alone. */ -static int rewrite_page(int log) -{ - int err = 0; - struct mtd_oob_ops ops; - - if (log) - pr_info("rewrite page\n"); - - ops.mode = MTD_OPS_RAW; /* No ECC */ - ops.len = mtd->writesize; - ops.retlen = 0; - ops.ooblen = 0; - ops.oobretlen = 0; - ops.ooboffs = 0; - ops.datbuf = wbuffer; - ops.oobbuf = NULL; - - err = mtd_write_oob(mtd, offset, &ops); - if (err || ops.retlen != mtd->writesize) { - pr_err("error: write_oob failed (%d)\n", err); - if (!err) - err = -EIO; - } - - return err; -} - -/* Reads page into rbuffer. Returns number of corrected bit errors (>=0) - * or error (<0) */ -static int read_page(int log) -{ - int err = 0; - size_t read; - struct mtd_ecc_stats oldstats; - - if (log) - pr_info("read_page\n"); - - /* Saving last mtd stats */ - memcpy(&oldstats, &mtd->ecc_stats, sizeof(oldstats)); - - err = mtd_read(mtd, offset, mtd->writesize, &read, rbuffer); - if (err == -EUCLEAN) - err = mtd->ecc_stats.corrected - oldstats.corrected; - - if (err < 0 || read != mtd->writesize) { - pr_err("error: read failed at %#llx\n", (long long)offset); - if (err >= 0) - err = -EIO; - } - - return err; -} - -/* Verifies rbuffer against random sequence */ -static int verify_page(int log) -{ - unsigned i, errs = 0; - - if (log) - pr_info("verify_page\n"); - - for (i = 0; i < mtd->writesize; i++) { - if (rbuffer[i] != hash(i+seed)) { - pr_err("Error: page offset %u, expected %02x, got %02x\n", - i, hash(i+seed), rbuffer[i]); - errs++; - } - } - - if (errs) - return -EIO; - else - return 0; -} - -#define CBIT(v, n) ((v) & (1 << (n))) -#define BCLR(v, n) ((v) = (v) & ~(1 << (n))) - -/* Finds the first '1' bit in wbuffer starting at offset 'byte' - * and sets it to '0'. */ -static int insert_biterror(unsigned byte) -{ - int bit; - - while (byte < mtd->writesize) { - for (bit = 7; bit >= 0; bit--) { - if (CBIT(wbuffer[byte], bit)) { - BCLR(wbuffer[byte], bit); - pr_info("Inserted biterror @ %u/%u\n", byte, bit); - return 0; - } - } - byte++; - } - pr_err("biterror: Failed to find a '1' bit\n"); - return -EIO; -} - -/* Writes 'random' data to page and then introduces deliberate bit - * errors into the page, while verifying each step. */ -static int incremental_errors_test(void) -{ - int err = 0; - unsigned i; - unsigned errs_per_subpage = 0; - - pr_info("incremental biterrors test\n"); - - for (i = 0; i < mtd->writesize; i++) - wbuffer[i] = hash(i+seed); - - err = write_page(1); - if (err) - goto exit; - - while (1) { - - err = rewrite_page(1); - if (err) - goto exit; - - err = read_page(1); - if (err > 0) - pr_info("Read reported %d corrected bit errors\n", err); - if (err < 0) { - pr_err("After %d biterrors per subpage, read reported error %d\n", - errs_per_subpage, err); - err = 0; - goto exit; - } - - err = verify_page(1); - if (err) { - pr_err("ECC failure, read data is incorrect despite read success\n"); - goto exit; - } - - pr_info("Successfully corrected %d bit errors per subpage\n", - errs_per_subpage); - - for (i = 0; i < subcount; i++) { - err = insert_biterror(i * subsize); - if (err < 0) - goto exit; - } - errs_per_subpage++; - } - -exit: - return err; -} - - -/* Writes 'random' data to page and then re-writes that same data repeatedly. - This eventually develops bit errors (bits written as '1' will slowly become - '0'), which are corrected as far as the ECC is capable of. */ -static int overwrite_test(void) -{ - int err = 0; - unsigned i; - unsigned max_corrected = 0; - unsigned opno = 0; - /* We don't expect more than this many correctable bit errors per - * page. */ - #define MAXBITS 512 - static unsigned bitstats[MAXBITS]; /* bit error histogram. */ - - memset(bitstats, 0, sizeof(bitstats)); - - pr_info("overwrite biterrors test\n"); - - for (i = 0; i < mtd->writesize; i++) - wbuffer[i] = hash(i+seed); - - err = write_page(1); - if (err) - goto exit; - - while (opno < max_overwrite) { - - err = rewrite_page(0); - if (err) - break; - - err = read_page(0); - if (err >= 0) { - if (err >= MAXBITS) { - pr_info("Implausible number of bit errors corrected\n"); - err = -EIO; - break; - } - bitstats[err]++; - if (err > max_corrected) { - max_corrected = err; - pr_info("Read reported %d corrected bit errors\n", - err); - } - } else { /* err < 0 */ - pr_info("Read reported error %d\n", err); - err = 0; - break; - } - - err = verify_page(0); - if (err) { - bitstats[max_corrected] = opno; - pr_info("ECC failure, read data is incorrect despite read success\n"); - break; - } - - opno++; - } - - /* At this point bitstats[0] contains the number of ops with no bit - * errors, bitstats[1] the number of ops with 1 bit error, etc. */ - pr_info("Bit error histogram (%d operations total):\n", opno); - for (i = 0; i < max_corrected; i++) - pr_info("Page reads with %3d corrected bit errors: %d\n", - i, bitstats[i]); - -exit: - return err; -} - -static int __init mtd_nandbiterrs_init(void) -{ - int err = 0; - - printk("\n"); - printk(KERN_INFO "==================================================\n"); - pr_info("MTD device: %d\n", dev); - - mtd = get_mtd_device(NULL, dev); - if (IS_ERR(mtd)) { - err = PTR_ERR(mtd); - pr_err("error: cannot get MTD device\n"); - goto exit_mtddev; - } - - if (mtd->type != MTD_NANDFLASH) { - pr_info("this test requires NAND flash\n"); - err = -ENODEV; - goto exit_nand; - } - - pr_info("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n", - (unsigned long long)mtd->size, mtd->erasesize, - mtd->writesize, mtd->oobsize); - - subsize = mtd->writesize >> mtd->subpage_sft; - subcount = mtd->writesize / subsize; - - pr_info("Device uses %d subpages of %d bytes\n", subcount, subsize); - - offset = page_offset * mtd->writesize; - eraseblock = mtd_div_by_eb(offset, mtd); - - pr_info("Using page=%u, offset=%llu, eraseblock=%u\n", - page_offset, offset, eraseblock); - - wbuffer = kmalloc(mtd->writesize, GFP_KERNEL); - if (!wbuffer) { - err = -ENOMEM; - goto exit_wbuffer; - } - - rbuffer = kmalloc(mtd->writesize, GFP_KERNEL); - if (!rbuffer) { - err = -ENOMEM; - goto exit_rbuffer; - } - - err = erase_block(); - if (err) - goto exit_error; - - if (mode == 0) - err = incremental_errors_test(); - else - err = overwrite_test(); - - if (err) - goto exit_error; - - /* We leave the block un-erased in case of test failure. */ - err = erase_block(); - if (err) - goto exit_error; - - err = -EIO; - pr_info("finished successfully.\n"); - printk(KERN_INFO "==================================================\n"); - -exit_error: - kfree(rbuffer); -exit_rbuffer: - kfree(wbuffer); -exit_wbuffer: - /* Nothing */ -exit_nand: - put_mtd_device(mtd); -exit_mtddev: - return err; -} - -static void __exit mtd_nandbiterrs_exit(void) -{ - return; -} - -module_init(mtd_nandbiterrs_init); -module_exit(mtd_nandbiterrs_exit); - -MODULE_DESCRIPTION("NAND bit error recovery test"); -MODULE_AUTHOR("Iwo Mergler"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c deleted file mode 100644 index ab81e9a..0000000 --- a/drivers/mtd/tests/mtd_oobtest.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Copyright (C) 2006-2008 Nokia Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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; see the file COPYING. If not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Test OOB read and write on MTD device. - * - * Author: Adrian Hunter - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int dev = -EINVAL; -module_param(dev, int, S_IRUGO); -MODULE_PARM_DESC(dev, "MTD device number to use"); - -static struct mtd_info *mtd; -static unsigned char *readbuf; -static unsigned char *writebuf; -static unsigned char *bbt; - -static int ebcnt; -static int pgcnt; -static int errcnt; -static int use_offset; -static int use_len; -static int use_len_max; -static int vary_offset; -static struct rnd_state rnd_state; - -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", ebnum); - return -EIO; - } - - return 0; -} - -static int erase_whole_device(void) -{ - int err; - unsigned int i; - - pr_info("erasing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - return err; - cond_resched(); - } - pr_info("erased %u eraseblocks\n", i); - return 0; -} - -static void do_vary_offset(void) -{ - use_len -= 1; - if (use_len < 1) { - use_offset += 1; - if (use_offset >= use_len_max) - use_offset = 0; - use_len = use_len_max - use_offset; - } -} - -static int write_eraseblock(int ebnum) -{ - int i; - struct mtd_oob_ops ops; - int err = 0; - loff_t addr = ebnum * mtd->erasesize; - - for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { - prandom_bytes_state(&rnd_state, writebuf, use_len); - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = use_len; - ops.oobretlen = 0; - ops.ooboffs = use_offset; - ops.datbuf = NULL; - ops.oobbuf = writebuf; - err = mtd_write_oob(mtd, addr, &ops); - if (err || ops.oobretlen != use_len) { - pr_err("error: writeoob failed at %#llx\n", - (long long)addr); - pr_err("error: use_len %d, use_offset %d\n", - use_len, use_offset); - errcnt += 1; - return err ? err : -1; - } - if (vary_offset) - do_vary_offset(); - } - - return err; -} - -static int write_whole_device(void) -{ - int err; - unsigned int i; - - pr_info("writing OOBs of whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = write_eraseblock(i); - if (err) - return err; - if (i % 256 == 0) - pr_info("written up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("written %u eraseblocks\n", i); - return 0; -} - -static int verify_eraseblock(int ebnum) -{ - int i; - struct mtd_oob_ops ops; - int err = 0; - loff_t addr = ebnum * mtd->erasesize; - - for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { - prandom_bytes_state(&rnd_state, writebuf, use_len); - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = use_len; - ops.oobretlen = 0; - ops.ooboffs = use_offset; - ops.datbuf = NULL; - ops.oobbuf = readbuf; - err = mtd_read_oob(mtd, addr, &ops); - if (err || ops.oobretlen != use_len) { - pr_err("error: readoob failed at %#llx\n", - (long long)addr); - errcnt += 1; - return err ? err : -1; - } - if (memcmp(readbuf, writebuf, use_len)) { - pr_err("error: verify failed at %#llx\n", - (long long)addr); - errcnt += 1; - if (errcnt > 1000) { - pr_err("error: too many errors\n"); - return -1; - } - } - if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) { - int k; - - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = mtd->ecclayout->oobavail; - ops.oobretlen = 0; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.oobbuf = readbuf; - err = mtd_read_oob(mtd, addr, &ops); - if (err || ops.oobretlen != mtd->ecclayout->oobavail) { - pr_err("error: readoob failed at %#llx\n", - (long long)addr); - errcnt += 1; - return err ? err : -1; - } - if (memcmp(readbuf + use_offset, writebuf, use_len)) { - pr_err("error: verify failed at %#llx\n", - (long long)addr); - errcnt += 1; - if (errcnt > 1000) { - pr_err("error: too many errors\n"); - return -1; - } - } - for (k = 0; k < use_offset; ++k) - if (readbuf[k] != 0xff) { - pr_err("error: verify 0xff " - "failed at %#llx\n", - (long long)addr); - errcnt += 1; - if (errcnt > 1000) { - pr_err("error: too " - "many errors\n"); - return -1; - } - } - for (k = use_offset + use_len; - k < mtd->ecclayout->oobavail; ++k) - if (readbuf[k] != 0xff) { - pr_err("error: verify 0xff " - "failed at %#llx\n", - (long long)addr); - errcnt += 1; - if (errcnt > 1000) { - pr_err("error: too " - "many errors\n"); - return -1; - } - } - } - if (vary_offset) - do_vary_offset(); - } - return err; -} - -static int verify_eraseblock_in_one_go(int ebnum) -{ - struct mtd_oob_ops ops; - int err = 0; - loff_t addr = ebnum * mtd->erasesize; - size_t len = mtd->ecclayout->oobavail * pgcnt; - - prandom_bytes_state(&rnd_state, writebuf, len); - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = len; - ops.oobretlen = 0; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.oobbuf = readbuf; - err = mtd_read_oob(mtd, addr, &ops); - if (err || ops.oobretlen != len) { - pr_err("error: readoob failed at %#llx\n", - (long long)addr); - errcnt += 1; - return err ? err : -1; - } - if (memcmp(readbuf, writebuf, len)) { - pr_err("error: verify failed at %#llx\n", - (long long)addr); - errcnt += 1; - if (errcnt > 1000) { - pr_err("error: too many errors\n"); - return -1; - } - } - - return err; -} - -static int verify_all_eraseblocks(void) -{ - int err; - unsigned int i; - - pr_info("verifying all eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = verify_eraseblock(i); - if (err) - return err; - if (i % 256 == 0) - pr_info("verified up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("verified %u eraseblocks\n", i); - return 0; -} - -static int is_block_bad(int ebnum) -{ - int ret; - loff_t addr = ebnum * mtd->erasesize; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kmalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - -static int __init mtd_oobtest_init(void) -{ - int err = 0; - unsigned int i; - uint64_t tmp; - struct mtd_oob_ops ops; - loff_t addr = 0, addr0; - - printk(KERN_INFO "\n"); - printk(KERN_INFO "=================================================\n"); - - if (dev < 0) { - pr_info("Please specify a valid mtd-device via module parameter\n"); - pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); - return -EINVAL; - } - - pr_info("MTD device: %d\n", dev); - - mtd = get_mtd_device(NULL, dev); - if (IS_ERR(mtd)) { - err = PTR_ERR(mtd); - pr_err("error: cannot get MTD device\n"); - return err; - } - - if (mtd->type != MTD_NANDFLASH) { - pr_info("this test requires NAND flash\n"); - goto out; - } - - tmp = mtd->size; - do_div(tmp, mtd->erasesize); - ebcnt = tmp; - pgcnt = mtd->erasesize / mtd->writesize; - - pr_info("MTD device size %llu, eraseblock size %u, " - "page size %u, count of eraseblocks %u, pages per " - "eraseblock %u, OOB size %u\n", - (unsigned long long)mtd->size, mtd->erasesize, - mtd->writesize, ebcnt, pgcnt, mtd->oobsize); - - err = -ENOMEM; - readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!readbuf) - goto out; - writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!writebuf) - goto out; - - err = scan_for_bad_eraseblocks(); - if (err) - goto out; - - use_offset = 0; - use_len = mtd->ecclayout->oobavail; - use_len_max = mtd->ecclayout->oobavail; - vary_offset = 0; - - /* First test: write all OOB, read it back and verify */ - pr_info("test 1 of 5\n"); - - err = erase_whole_device(); - if (err) - goto out; - - prandom_seed_state(&rnd_state, 1); - err = write_whole_device(); - if (err) - goto out; - - prandom_seed_state(&rnd_state, 1); - err = verify_all_eraseblocks(); - if (err) - goto out; - - /* - * Second test: write all OOB, a block at a time, read it back and - * verify. - */ - pr_info("test 2 of 5\n"); - - err = erase_whole_device(); - if (err) - goto out; - - prandom_seed_state(&rnd_state, 3); - err = write_whole_device(); - if (err) - goto out; - - /* Check all eraseblocks */ - prandom_seed_state(&rnd_state, 3); - pr_info("verifying all eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = verify_eraseblock_in_one_go(i); - if (err) - goto out; - if (i % 256 == 0) - pr_info("verified up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("verified %u eraseblocks\n", i); - - /* - * Third test: write OOB at varying offsets and lengths, read it back - * and verify. - */ - pr_info("test 3 of 5\n"); - - err = erase_whole_device(); - if (err) - goto out; - - /* Write all eraseblocks */ - use_offset = 0; - use_len = mtd->ecclayout->oobavail; - use_len_max = mtd->ecclayout->oobavail; - vary_offset = 1; - prandom_seed_state(&rnd_state, 5); - - err = write_whole_device(); - if (err) - goto out; - - /* Check all eraseblocks */ - use_offset = 0; - use_len = mtd->ecclayout->oobavail; - use_len_max = mtd->ecclayout->oobavail; - vary_offset = 1; - prandom_seed_state(&rnd_state, 5); - err = verify_all_eraseblocks(); - if (err) - goto out; - - use_offset = 0; - use_len = mtd->ecclayout->oobavail; - use_len_max = mtd->ecclayout->oobavail; - vary_offset = 0; - - /* Fourth test: try to write off end of device */ - pr_info("test 4 of 5\n"); - - err = erase_whole_device(); - if (err) - goto out; - - addr0 = 0; - for (i = 0; i < ebcnt && bbt[i]; ++i) - addr0 += mtd->erasesize; - - /* Attempt to write off end of OOB */ - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = 1; - ops.oobretlen = 0; - ops.ooboffs = mtd->ecclayout->oobavail; - ops.datbuf = NULL; - ops.oobbuf = writebuf; - pr_info("attempting to start write past end of OOB\n"); - pr_info("an error is expected...\n"); - err = mtd_write_oob(mtd, addr0, &ops); - if (err) { - pr_info("error occurred as expected\n"); - err = 0; - } else { - pr_err("error: can write past end of OOB\n"); - errcnt += 1; - } - - /* Attempt to read off end of OOB */ - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = 1; - ops.oobretlen = 0; - ops.ooboffs = mtd->ecclayout->oobavail; - ops.datbuf = NULL; - ops.oobbuf = readbuf; - pr_info("attempting to start read past end of OOB\n"); - pr_info("an error is expected...\n"); - err = mtd_read_oob(mtd, addr0, &ops); - if (err) { - pr_info("error occurred as expected\n"); - err = 0; - } else { - pr_err("error: can read past end of OOB\n"); - errcnt += 1; - } - - if (bbt[ebcnt - 1]) - pr_info("skipping end of device tests because last " - "block is bad\n"); - else { - /* Attempt to write off end of device */ - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = mtd->ecclayout->oobavail + 1; - ops.oobretlen = 0; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.oobbuf = writebuf; - pr_info("attempting to write past end of device\n"); - pr_info("an error is expected...\n"); - err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); - if (err) { - pr_info("error occurred as expected\n"); - err = 0; - } else { - pr_err("error: wrote past end of device\n"); - errcnt += 1; - } - - /* Attempt to read off end of device */ - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = mtd->ecclayout->oobavail + 1; - ops.oobretlen = 0; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.oobbuf = readbuf; - pr_info("attempting to read past end of device\n"); - pr_info("an error is expected...\n"); - err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); - if (err) { - pr_info("error occurred as expected\n"); - err = 0; - } else { - pr_err("error: read past end of device\n"); - errcnt += 1; - } - - err = erase_eraseblock(ebcnt - 1); - if (err) - goto out; - - /* Attempt to write off end of device */ - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = mtd->ecclayout->oobavail; - ops.oobretlen = 0; - ops.ooboffs = 1; - ops.datbuf = NULL; - ops.oobbuf = writebuf; - pr_info("attempting to write past end of device\n"); - pr_info("an error is expected...\n"); - err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); - if (err) { - pr_info("error occurred as expected\n"); - err = 0; - } else { - pr_err("error: wrote past end of device\n"); - errcnt += 1; - } - - /* Attempt to read off end of device */ - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = mtd->ecclayout->oobavail; - ops.oobretlen = 0; - ops.ooboffs = 1; - ops.datbuf = NULL; - ops.oobbuf = readbuf; - pr_info("attempting to read past end of device\n"); - pr_info("an error is expected...\n"); - err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); - if (err) { - pr_info("error occurred as expected\n"); - err = 0; - } else { - pr_err("error: read past end of device\n"); - errcnt += 1; - } - } - - /* Fifth test: write / read across block boundaries */ - pr_info("test 5 of 5\n"); - - /* Erase all eraseblocks */ - err = erase_whole_device(); - if (err) - goto out; - - /* Write all eraseblocks */ - prandom_seed_state(&rnd_state, 11); - pr_info("writing OOBs of whole device\n"); - for (i = 0; i < ebcnt - 1; ++i) { - int cnt = 2; - int pg; - size_t sz = mtd->ecclayout->oobavail; - if (bbt[i] || bbt[i + 1]) - continue; - addr = (i + 1) * mtd->erasesize - mtd->writesize; - for (pg = 0; pg < cnt; ++pg) { - prandom_bytes_state(&rnd_state, writebuf, sz); - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = sz; - ops.oobretlen = 0; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.oobbuf = writebuf; - err = mtd_write_oob(mtd, addr, &ops); - if (err) - goto out; - if (i % 256 == 0) - pr_info("written up to eraseblock %u\n", i); - cond_resched(); - addr += mtd->writesize; - } - } - pr_info("written %u eraseblocks\n", i); - - /* Check all eraseblocks */ - prandom_seed_state(&rnd_state, 11); - pr_info("verifying all eraseblocks\n"); - for (i = 0; i < ebcnt - 1; ++i) { - if (bbt[i] || bbt[i + 1]) - continue; - prandom_bytes_state(&rnd_state, writebuf, - mtd->ecclayout->oobavail * 2); - addr = (i + 1) * mtd->erasesize - mtd->writesize; - ops.mode = MTD_OPS_AUTO_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = mtd->ecclayout->oobavail * 2; - ops.oobretlen = 0; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.oobbuf = readbuf; - err = mtd_read_oob(mtd, addr, &ops); - if (err) - goto out; - if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) { - pr_err("error: verify failed at %#llx\n", - (long long)addr); - errcnt += 1; - if (errcnt > 1000) { - pr_err("error: too many errors\n"); - goto out; - } - } - if (i % 256 == 0) - pr_info("verified up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("verified %u eraseblocks\n", i); - - pr_info("finished with %d errors\n", errcnt); -out: - kfree(bbt); - kfree(writebuf); - kfree(readbuf); - put_mtd_device(mtd); - if (err) - pr_info("error %d occurred\n", err); - printk(KERN_INFO "=================================================\n"); - return err; -} -module_init(mtd_oobtest_init); - -static void __exit mtd_oobtest_exit(void) -{ - return; -} -module_exit(mtd_oobtest_exit); - -MODULE_DESCRIPTION("Out-of-band test module"); -MODULE_AUTHOR("Adrian Hunter"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c deleted file mode 100644 index acd991f..0000000 --- a/drivers/mtd/tests/mtd_pagetest.c +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Copyright (C) 2006-2008 Nokia Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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; see the file COPYING. If not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Test page read and write on MTD device. - * - * Author: Adrian Hunter - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int dev = -EINVAL; -module_param(dev, int, S_IRUGO); -MODULE_PARM_DESC(dev, "MTD device number to use"); - -static struct mtd_info *mtd; -static unsigned char *twopages; -static unsigned char *writebuf; -static unsigned char *boundary; -static unsigned char *bbt; - -static int pgsize; -static int bufsize; -static int ebcnt; -static int pgcnt; -static int errcnt; -static struct rnd_state rnd_state; - -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -static int write_eraseblock(int ebnum) -{ - int err = 0; - size_t written; - loff_t addr = ebnum * mtd->erasesize; - - prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); - cond_resched(); - err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf); - if (err || written != mtd->erasesize) - pr_err("error: write failed at %#llx\n", - (long long)addr); - - return err; -} - -static int verify_eraseblock(int ebnum) -{ - uint32_t j; - size_t read; - int err = 0, i; - loff_t addr0, addrn; - loff_t addr = ebnum * mtd->erasesize; - - addr0 = 0; - for (i = 0; i < ebcnt && bbt[i]; ++i) - addr0 += mtd->erasesize; - - addrn = mtd->size; - for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) - addrn -= mtd->erasesize; - - prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); - for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { - /* Do a read to set the internal dataRAMs to different data */ - err = mtd_read(mtd, addr0, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); - return err; - } - err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)(addrn - bufsize)); - return err; - } - memset(twopages, 0, bufsize); - err = mtd_read(mtd, addr, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); - break; - } - if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) { - pr_err("error: verify failed at %#llx\n", - (long long)addr); - errcnt += 1; - } - } - /* Check boundary between eraseblocks */ - if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) { - struct rnd_state old_state = rnd_state; - - /* Do a read to set the internal dataRAMs to different data */ - err = mtd_read(mtd, addr0, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); - return err; - } - err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)(addrn - bufsize)); - return err; - } - memset(twopages, 0, bufsize); - err = mtd_read(mtd, addr, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); - return err; - } - memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); - prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize); - if (memcmp(twopages, boundary, bufsize)) { - pr_err("error: verify failed at %#llx\n", - (long long)addr); - errcnt += 1; - } - rnd_state = old_state; - } - return err; -} - -static int crosstest(void) -{ - size_t read; - int err = 0, i; - loff_t addr, addr0, addrn; - unsigned char *pp1, *pp2, *pp3, *pp4; - - pr_info("crosstest\n"); - pp1 = kmalloc(pgsize * 4, GFP_KERNEL); - if (!pp1) - return -ENOMEM; - pp2 = pp1 + pgsize; - pp3 = pp2 + pgsize; - pp4 = pp3 + pgsize; - memset(pp1, 0, pgsize * 4); - - addr0 = 0; - for (i = 0; i < ebcnt && bbt[i]; ++i) - addr0 += mtd->erasesize; - - addrn = mtd->size; - for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) - addrn -= mtd->erasesize; - - /* Read 2nd-to-last page to pp1 */ - addr = addrn - pgsize - pgsize; - err = mtd_read(mtd, addr, pgsize, &read, pp1); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); - kfree(pp1); - return err; - } - - /* Read 3rd-to-last page to pp1 */ - addr = addrn - pgsize - pgsize - pgsize; - err = mtd_read(mtd, addr, pgsize, &read, pp1); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); - kfree(pp1); - return err; - } - - /* Read first page to pp2 */ - addr = addr0; - pr_info("reading page at %#llx\n", (long long)addr); - err = mtd_read(mtd, addr, pgsize, &read, pp2); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); - kfree(pp1); - return err; - } - - /* Read last page to pp3 */ - addr = addrn - pgsize; - pr_info("reading page at %#llx\n", (long long)addr); - err = mtd_read(mtd, addr, pgsize, &read, pp3); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); - kfree(pp1); - return err; - } - - /* Read first page again to pp4 */ - addr = addr0; - pr_info("reading page at %#llx\n", (long long)addr); - err = mtd_read(mtd, addr, pgsize, &read, pp4); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); - kfree(pp1); - return err; - } - - /* pp2 and pp4 should be the same */ - pr_info("verifying pages read at %#llx match\n", - (long long)addr0); - if (memcmp(pp2, pp4, pgsize)) { - pr_err("verify failed!\n"); - errcnt += 1; - } else if (!err) - pr_info("crosstest ok\n"); - kfree(pp1); - return err; -} - -static int erasecrosstest(void) -{ - size_t read, written; - int err = 0, i, ebnum, ebnum2; - loff_t addr0; - char *readbuf = twopages; - - pr_info("erasecrosstest\n"); - - ebnum = 0; - addr0 = 0; - for (i = 0; i < ebcnt && bbt[i]; ++i) { - addr0 += mtd->erasesize; - ebnum += 1; - } - - ebnum2 = ebcnt - 1; - while (ebnum2 && bbt[ebnum2]) - ebnum2 -= 1; - - pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); - if (err) - return err; - - pr_info("writing 1st page of block %d\n", ebnum); - prandom_bytes_state(&rnd_state, writebuf, pgsize); - strcpy(writebuf, "There is no data like this!"); - err = mtd_write(mtd, addr0, pgsize, &written, writebuf); - if (err || written != pgsize) { - pr_info("error: write failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } - - pr_info("reading 1st page of block %d\n", ebnum); - memset(readbuf, 0, pgsize); - err = mtd_read(mtd, addr0, pgsize, &read, readbuf); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } - - pr_info("verifying 1st page of block %d\n", ebnum); - if (memcmp(writebuf, readbuf, pgsize)) { - pr_err("verify failed!\n"); - errcnt += 1; - return -1; - } - - pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); - if (err) - return err; - - pr_info("writing 1st page of block %d\n", ebnum); - prandom_bytes_state(&rnd_state, writebuf, pgsize); - strcpy(writebuf, "There is no data like this!"); - err = mtd_write(mtd, addr0, pgsize, &written, writebuf); - if (err || written != pgsize) { - pr_err("error: write failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } - - pr_info("erasing block %d\n", ebnum2); - err = erase_eraseblock(ebnum2); - if (err) - return err; - - pr_info("reading 1st page of block %d\n", ebnum); - memset(readbuf, 0, pgsize); - err = mtd_read(mtd, addr0, pgsize, &read, readbuf); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } - - pr_info("verifying 1st page of block %d\n", ebnum); - if (memcmp(writebuf, readbuf, pgsize)) { - pr_err("verify failed!\n"); - errcnt += 1; - return -1; - } - - if (!err) - pr_info("erasecrosstest ok\n"); - return err; -} - -static int erasetest(void) -{ - size_t read, written; - int err = 0, i, ebnum, ok = 1; - loff_t addr0; - - pr_info("erasetest\n"); - - ebnum = 0; - addr0 = 0; - for (i = 0; i < ebcnt && bbt[i]; ++i) { - addr0 += mtd->erasesize; - ebnum += 1; - } - - pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); - if (err) - return err; - - pr_info("writing 1st page of block %d\n", ebnum); - prandom_bytes_state(&rnd_state, writebuf, pgsize); - err = mtd_write(mtd, addr0, pgsize, &written, writebuf); - if (err || written != pgsize) { - pr_err("error: write failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } - - pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); - if (err) - return err; - - pr_info("reading 1st page of block %d\n", ebnum); - err = mtd_read(mtd, addr0, pgsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } - - pr_info("verifying 1st page of block %d is all 0xff\n", - ebnum); - for (i = 0; i < pgsize; ++i) - if (twopages[i] != 0xff) { - pr_err("verifying all 0xff failed at %d\n", - i); - errcnt += 1; - ok = 0; - break; - } - - if (ok && !err) - pr_info("erasetest ok\n"); - - return err; -} - -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - -static int __init mtd_pagetest_init(void) -{ - int err = 0; - uint64_t tmp; - uint32_t i; - - printk(KERN_INFO "\n"); - printk(KERN_INFO "=================================================\n"); - - if (dev < 0) { - pr_info("Please specify a valid mtd-device via module parameter\n"); - pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); - return -EINVAL; - } - - pr_info("MTD device: %d\n", dev); - - mtd = get_mtd_device(NULL, dev); - if (IS_ERR(mtd)) { - err = PTR_ERR(mtd); - pr_err("error: cannot get MTD device\n"); - return err; - } - - if (mtd->type != MTD_NANDFLASH) { - pr_info("this test requires NAND flash\n"); - goto out; - } - - tmp = mtd->size; - do_div(tmp, mtd->erasesize); - ebcnt = tmp; - pgcnt = mtd->erasesize / mtd->writesize; - pgsize = mtd->writesize; - - pr_info("MTD device size %llu, eraseblock size %u, " - "page size %u, count of eraseblocks %u, pages per " - "eraseblock %u, OOB size %u\n", - (unsigned long long)mtd->size, mtd->erasesize, - pgsize, ebcnt, pgcnt, mtd->oobsize); - - err = -ENOMEM; - bufsize = pgsize * 2; - writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!writebuf) - goto out; - twopages = kmalloc(bufsize, GFP_KERNEL); - if (!twopages) - goto out; - boundary = kmalloc(bufsize, GFP_KERNEL); - if (!boundary) - goto out; - - err = scan_for_bad_eraseblocks(); - if (err) - goto out; - - /* Erase all eraseblocks */ - pr_info("erasing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - goto out; - cond_resched(); - } - pr_info("erased %u eraseblocks\n", i); - - /* Write all eraseblocks */ - prandom_seed_state(&rnd_state, 1); - pr_info("writing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = write_eraseblock(i); - if (err) - goto out; - if (i % 256 == 0) - pr_info("written up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("written %u eraseblocks\n", i); - - /* Check all eraseblocks */ - prandom_seed_state(&rnd_state, 1); - pr_info("verifying all eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = verify_eraseblock(i); - if (err) - goto out; - if (i % 256 == 0) - pr_info("verified up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("verified %u eraseblocks\n", i); - - err = crosstest(); - if (err) - goto out; - - err = erasecrosstest(); - if (err) - goto out; - - err = erasetest(); - if (err) - goto out; - - pr_info("finished with %d errors\n", errcnt); -out: - - kfree(bbt); - kfree(boundary); - kfree(twopages); - kfree(writebuf); - put_mtd_device(mtd); - if (err) - pr_info("error %d occurred\n", err); - printk(KERN_INFO "=================================================\n"); - return err; -} -module_init(mtd_pagetest_init); - -static void __exit mtd_pagetest_exit(void) -{ - return; -} -module_exit(mtd_pagetest_exit); - -MODULE_DESCRIPTION("NAND page test"); -MODULE_AUTHOR("Adrian Hunter"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c deleted file mode 100644 index 2cdd0c4..0000000 --- a/drivers/mtd/tests/mtd_readtest.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2006-2008 Nokia Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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; see the file COPYING. If not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Check MTD device read. - * - * Author: Adrian Hunter - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -static int dev = -EINVAL; -module_param(dev, int, S_IRUGO); -MODULE_PARM_DESC(dev, "MTD device number to use"); - -static struct mtd_info *mtd; -static unsigned char *iobuf; -static unsigned char *iobuf1; -static unsigned char *bbt; - -static int pgsize; -static int ebcnt; -static int pgcnt; - -static int read_eraseblock_by_page(int ebnum) -{ - size_t read; - int i, ret, err = 0; - loff_t addr = ebnum * mtd->erasesize; - void *buf = iobuf; - void *oobbuf = iobuf1; - - for (i = 0; i < pgcnt; i++) { - memset(buf, 0 , pgsize); - ret = mtd_read(mtd, addr, pgsize, &read, buf); - if (ret == -EUCLEAN) - ret = 0; - if (ret || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); - if (!err) - err = ret; - if (!err) - err = -EINVAL; - } - if (mtd->oobsize) { - struct mtd_oob_ops ops; - - ops.mode = MTD_OPS_PLACE_OOB; - ops.len = 0; - ops.retlen = 0; - ops.ooblen = mtd->oobsize; - ops.oobretlen = 0; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.oobbuf = oobbuf; - ret = mtd_read_oob(mtd, addr, &ops); - if ((ret && !mtd_is_bitflip(ret)) || - ops.oobretlen != mtd->oobsize) { - pr_err("error: read oob failed at " - "%#llx\n", (long long)addr); - if (!err) - err = ret; - if (!err) - err = -EINVAL; - } - oobbuf += mtd->oobsize; - } - addr += pgsize; - buf += pgsize; - } - - return err; -} - -static void dump_eraseblock(int ebnum) -{ - int i, j, n; - char line[128]; - int pg, oob; - - pr_info("dumping eraseblock %d\n", ebnum); - n = mtd->erasesize; - for (i = 0; i < n;) { - char *p = line; - - p += sprintf(p, "%05x: ", i); - for (j = 0; j < 32 && i < n; j++, i++) - p += sprintf(p, "%02x", (unsigned int)iobuf[i]); - printk(KERN_CRIT "%s\n", line); - cond_resched(); - } - if (!mtd->oobsize) - return; - pr_info("dumping oob from eraseblock %d\n", ebnum); - n = mtd->oobsize; - for (pg = 0, i = 0; pg < pgcnt; pg++) - for (oob = 0; oob < n;) { - char *p = line; - - p += sprintf(p, "%05x: ", i); - for (j = 0; j < 32 && oob < n; j++, oob++, i++) - p += sprintf(p, "%02x", - (unsigned int)iobuf1[i]); - printk(KERN_CRIT "%s\n", line); - cond_resched(); - } -} - -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - if (!mtd_can_have_bb(mtd)) - return 0; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - -static int __init mtd_readtest_init(void) -{ - uint64_t tmp; - int err, i; - - printk(KERN_INFO "\n"); - printk(KERN_INFO "=================================================\n"); - - if (dev < 0) { - pr_info("Please specify a valid mtd-device via module parameter\n"); - return -EINVAL; - } - - pr_info("MTD device: %d\n", dev); - - mtd = get_mtd_device(NULL, dev); - if (IS_ERR(mtd)) { - err = PTR_ERR(mtd); - pr_err("error: Cannot get MTD device\n"); - return err; - } - - if (mtd->writesize == 1) { - pr_info("not NAND flash, assume page size is 512 " - "bytes.\n"); - pgsize = 512; - } else - pgsize = mtd->writesize; - - tmp = mtd->size; - do_div(tmp, mtd->erasesize); - ebcnt = tmp; - pgcnt = mtd->erasesize / pgsize; - - pr_info("MTD device size %llu, eraseblock size %u, " - "page size %u, count of eraseblocks %u, pages per " - "eraseblock %u, OOB size %u\n", - (unsigned long long)mtd->size, mtd->erasesize, - pgsize, ebcnt, pgcnt, mtd->oobsize); - - err = -ENOMEM; - iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!iobuf) - goto out; - iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!iobuf1) - goto out; - - err = scan_for_bad_eraseblocks(); - if (err) - goto out; - - /* Read all eraseblocks 1 page at a time */ - pr_info("testing page read\n"); - for (i = 0; i < ebcnt; ++i) { - int ret; - - if (bbt[i]) - continue; - ret = read_eraseblock_by_page(i); - if (ret) { - dump_eraseblock(i); - if (!err) - err = ret; - } - cond_resched(); - } - - if (err) - pr_info("finished with errors\n"); - else - pr_info("finished\n"); - -out: - - kfree(iobuf); - kfree(iobuf1); - kfree(bbt); - put_mtd_device(mtd); - if (err) - pr_info("error %d occurred\n", err); - printk(KERN_INFO "=================================================\n"); - return err; -} -module_init(mtd_readtest_init); - -static void __exit mtd_readtest_exit(void) -{ - return; -} -module_exit(mtd_readtest_exit); - -MODULE_DESCRIPTION("Read test module"); -MODULE_AUTHOR("Adrian Hunter"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c deleted file mode 100644 index 20b63d1..0000000 --- a/drivers/mtd/tests/mtd_speedtest.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright (C) 2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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; see the file COPYING. If not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Test read and write speed of a MTD device. - * - * Author: Adrian Hunter - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -static int dev = -EINVAL; -module_param(dev, int, S_IRUGO); -MODULE_PARM_DESC(dev, "MTD device number to use"); - -static int count; -module_param(count, int, S_IRUGO); -MODULE_PARM_DESC(count, "Maximum number of eraseblocks to use " - "(0 means use all)"); - -static struct mtd_info *mtd; -static unsigned char *iobuf; -static unsigned char *bbt; - -static int pgsize; -static int ebcnt; -static int pgcnt; -static int goodebcnt; -static struct timeval start, finish; - - -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -static int multiblock_erase(int ebnum, int blocks) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize * blocks; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d, blocks %d\n", - err, ebnum, blocks); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d," - "blocks %d\n", ebnum, blocks); - return -EIO; - } - - return 0; -} - -static int erase_whole_device(void) -{ - int err; - unsigned int i; - - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - return err; - cond_resched(); - } - return 0; -} - -static int write_eraseblock(int ebnum) -{ - size_t written; - int err = 0; - loff_t addr = ebnum * mtd->erasesize; - - err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf); - if (err || written != mtd->erasesize) { - pr_err("error: write failed at %#llx\n", addr); - if (!err) - err = -EINVAL; - } - - return err; -} - -static int write_eraseblock_by_page(int ebnum) -{ - size_t written; - int i, err = 0; - loff_t addr = ebnum * mtd->erasesize; - void *buf = iobuf; - - for (i = 0; i < pgcnt; i++) { - err = mtd_write(mtd, addr, pgsize, &written, buf); - if (err || written != pgsize) { - pr_err("error: write failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; - break; - } - addr += pgsize; - buf += pgsize; - } - - return err; -} - -static int write_eraseblock_by_2pages(int ebnum) -{ - size_t written, sz = pgsize * 2; - int i, n = pgcnt / 2, err = 0; - loff_t addr = ebnum * mtd->erasesize; - void *buf = iobuf; - - for (i = 0; i < n; i++) { - err = mtd_write(mtd, addr, sz, &written, buf); - if (err || written != sz) { - pr_err("error: write failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; - return err; - } - addr += sz; - buf += sz; - } - if (pgcnt % 2) { - err = mtd_write(mtd, addr, pgsize, &written, buf); - if (err || written != pgsize) { - pr_err("error: write failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; - } - } - - return err; -} - -static int read_eraseblock(int ebnum) -{ - size_t read; - int err = 0; - loff_t addr = ebnum * mtd->erasesize; - - err = mtd_read(mtd, addr, mtd->erasesize, &read, iobuf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != mtd->erasesize) { - pr_err("error: read failed at %#llx\n", addr); - if (!err) - err = -EINVAL; - } - - return err; -} - -static int read_eraseblock_by_page(int ebnum) -{ - size_t read; - int i, err = 0; - loff_t addr = ebnum * mtd->erasesize; - void *buf = iobuf; - - for (i = 0; i < pgcnt; i++) { - err = mtd_read(mtd, addr, pgsize, &read, buf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; - break; - } - addr += pgsize; - buf += pgsize; - } - - return err; -} - -static int read_eraseblock_by_2pages(int ebnum) -{ - size_t read, sz = pgsize * 2; - int i, n = pgcnt / 2, err = 0; - loff_t addr = ebnum * mtd->erasesize; - void *buf = iobuf; - - for (i = 0; i < n; i++) { - err = mtd_read(mtd, addr, sz, &read, buf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != sz) { - pr_err("error: read failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; - return err; - } - addr += sz; - buf += sz; - } - if (pgcnt % 2) { - err = mtd_read(mtd, addr, pgsize, &read, buf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; - } - } - - return err; -} - -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static inline void start_timing(void) -{ - do_gettimeofday(&start); -} - -static inline void stop_timing(void) -{ - do_gettimeofday(&finish); -} - -static long calc_speed(void) -{ - uint64_t k; - long ms; - - ms = (finish.tv_sec - start.tv_sec) * 1000 + - (finish.tv_usec - start.tv_usec) / 1000; - if (ms == 0) - return 0; - k = goodebcnt * (mtd->erasesize / 1024) * 1000; - do_div(k, ms); - return k; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - if (!mtd_can_have_bb(mtd)) - goto out; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); -out: - goodebcnt = ebcnt - bad; - return 0; -} - -static int __init mtd_speedtest_init(void) -{ - int err, i, blocks, j, k; - long speed; - uint64_t tmp; - - printk(KERN_INFO "\n"); - printk(KERN_INFO "=================================================\n"); - - if (dev < 0) { - pr_info("Please specify a valid mtd-device via module parameter\n"); - pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); - return -EINVAL; - } - - if (count) - pr_info("MTD device: %d count: %d\n", dev, count); - else - pr_info("MTD device: %d\n", dev); - - mtd = get_mtd_device(NULL, dev); - if (IS_ERR(mtd)) { - err = PTR_ERR(mtd); - pr_err("error: cannot get MTD device\n"); - return err; - } - - if (mtd->writesize == 1) { - pr_info("not NAND flash, assume page size is 512 " - "bytes.\n"); - pgsize = 512; - } else - pgsize = mtd->writesize; - - tmp = mtd->size; - do_div(tmp, mtd->erasesize); - ebcnt = tmp; - pgcnt = mtd->erasesize / pgsize; - - pr_info("MTD device size %llu, eraseblock size %u, " - "page size %u, count of eraseblocks %u, pages per " - "eraseblock %u, OOB size %u\n", - (unsigned long long)mtd->size, mtd->erasesize, - pgsize, ebcnt, pgcnt, mtd->oobsize); - - if (count > 0 && count < ebcnt) - ebcnt = count; - - err = -ENOMEM; - iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!iobuf) - goto out; - - prandom_bytes(iobuf, mtd->erasesize); - - err = scan_for_bad_eraseblocks(); - if (err) - goto out; - - err = erase_whole_device(); - if (err) - goto out; - - /* Write all eraseblocks, 1 eraseblock at a time */ - pr_info("testing eraseblock write speed\n"); - start_timing(); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = write_eraseblock(i); - if (err) - goto out; - cond_resched(); - } - stop_timing(); - speed = calc_speed(); - pr_info("eraseblock write speed is %ld KiB/s\n", speed); - - /* Read all eraseblocks, 1 eraseblock at a time */ - pr_info("testing eraseblock read speed\n"); - start_timing(); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = read_eraseblock(i); - if (err) - goto out; - cond_resched(); - } - stop_timing(); - speed = calc_speed(); - pr_info("eraseblock read speed is %ld KiB/s\n", speed); - - err = erase_whole_device(); - if (err) - goto out; - - /* Write all eraseblocks, 1 page at a time */ - pr_info("testing page write speed\n"); - start_timing(); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = write_eraseblock_by_page(i); - if (err) - goto out; - cond_resched(); - } - stop_timing(); - speed = calc_speed(); - pr_info("page write speed is %ld KiB/s\n", speed); - - /* Read all eraseblocks, 1 page at a time */ - pr_info("testing page read speed\n"); - start_timing(); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = read_eraseblock_by_page(i); - if (err) - goto out; - cond_resched(); - } - stop_timing(); - speed = calc_speed(); - pr_info("page read speed is %ld KiB/s\n", speed); - - err = erase_whole_device(); - if (err) - goto out; - - /* Write all eraseblocks, 2 pages at a time */ - pr_info("testing 2 page write speed\n"); - start_timing(); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = write_eraseblock_by_2pages(i); - if (err) - goto out; - cond_resched(); - } - stop_timing(); - speed = calc_speed(); - pr_info("2 page write speed is %ld KiB/s\n", speed); - - /* Read all eraseblocks, 2 pages at a time */ - pr_info("testing 2 page read speed\n"); - start_timing(); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = read_eraseblock_by_2pages(i); - if (err) - goto out; - cond_resched(); - } - stop_timing(); - speed = calc_speed(); - pr_info("2 page read speed is %ld KiB/s\n", speed); - - /* Erase all eraseblocks */ - pr_info("Testing erase speed\n"); - start_timing(); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - goto out; - cond_resched(); - } - stop_timing(); - speed = calc_speed(); - pr_info("erase speed is %ld KiB/s\n", speed); - - /* Multi-block erase all eraseblocks */ - for (k = 1; k < 7; k++) { - blocks = 1 << k; - pr_info("Testing %dx multi-block erase speed\n", - blocks); - start_timing(); - for (i = 0; i < ebcnt; ) { - for (j = 0; j < blocks && (i + j) < ebcnt; j++) - if (bbt[i + j]) - break; - if (j < 1) { - i++; - continue; - } - err = multiblock_erase(i, j); - if (err) - goto out; - cond_resched(); - i += j; - } - stop_timing(); - speed = calc_speed(); - pr_info("%dx multi-block erase speed is %ld KiB/s\n", - blocks, speed); - } - pr_info("finished\n"); -out: - kfree(iobuf); - kfree(bbt); - put_mtd_device(mtd); - if (err) - pr_info("error %d occurred\n", err); - printk(KERN_INFO "=================================================\n"); - return err; -} -module_init(mtd_speedtest_init); - -static void __exit mtd_speedtest_exit(void) -{ - return; -} -module_exit(mtd_speedtest_exit); - -MODULE_DESCRIPTION("Speed test module"); -MODULE_AUTHOR("Adrian Hunter"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c deleted file mode 100644 index 3a95e61..0000000 --- a/drivers/mtd/tests/mtd_stresstest.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2006-2008 Nokia Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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; see the file COPYING. If not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Test random reads, writes and erases on MTD device. - * - * Author: Adrian Hunter - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int dev = -EINVAL; -module_param(dev, int, S_IRUGO); -MODULE_PARM_DESC(dev, "MTD device number to use"); - -static int count = 10000; -module_param(count, int, S_IRUGO); -MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)"); - -static struct mtd_info *mtd; -static unsigned char *writebuf; -static unsigned char *readbuf; -static unsigned char *bbt; -static int *offsets; - -static int pgsize; -static int bufsize; -static int ebcnt; -static int pgcnt; - -static int rand_eb(void) -{ - unsigned int eb; - -again: - eb = prandom_u32(); - /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ - eb %= (ebcnt - 1); - if (bbt[eb]) - goto again; - return eb; -} - -static int rand_offs(void) -{ - unsigned int offs; - - offs = prandom_u32(); - offs %= bufsize; - return offs; -} - -static int rand_len(int offs) -{ - unsigned int len; - - len = prandom_u32(); - len %= (bufsize - offs); - return len; -} - -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (unlikely(err)) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (unlikely(ei.state == MTD_ERASE_FAILED)) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int do_read(void) -{ - size_t read; - int eb = rand_eb(); - int offs = rand_offs(); - int len = rand_len(offs), err; - loff_t addr; - - if (bbt[eb + 1]) { - if (offs >= mtd->erasesize) - offs -= mtd->erasesize; - if (offs + len > mtd->erasesize) - len = mtd->erasesize - offs; - } - addr = eb * mtd->erasesize + offs; - err = mtd_read(mtd, addr, len, &read, readbuf); - if (mtd_is_bitflip(err)) - err = 0; - if (unlikely(err || read != len)) { - pr_err("error: read failed at 0x%llx\n", - (long long)addr); - if (!err) - err = -EINVAL; - return err; - } - return 0; -} - -static int do_write(void) -{ - int eb = rand_eb(), offs, err, len; - size_t written; - loff_t addr; - - offs = offsets[eb]; - if (offs >= mtd->erasesize) { - err = erase_eraseblock(eb); - if (err) - return err; - offs = offsets[eb] = 0; - } - len = rand_len(offs); - len = ((len + pgsize - 1) / pgsize) * pgsize; - if (offs + len > mtd->erasesize) { - if (bbt[eb + 1]) - len = mtd->erasesize - offs; - else { - err = erase_eraseblock(eb + 1); - if (err) - return err; - offsets[eb + 1] = 0; - } - } - addr = eb * mtd->erasesize + offs; - err = mtd_write(mtd, addr, len, &written, writebuf); - if (unlikely(err || written != len)) { - pr_err("error: write failed at 0x%llx\n", - (long long)addr); - if (!err) - err = -EINVAL; - return err; - } - offs += len; - while (offs > mtd->erasesize) { - offsets[eb++] = mtd->erasesize; - offs -= mtd->erasesize; - } - offsets[eb] = offs; - return 0; -} - -static int do_operation(void) -{ - if (prandom_u32() & 1) - return do_read(); - else - return do_write(); -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - if (!mtd_can_have_bb(mtd)) - return 0; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - -static int __init mtd_stresstest_init(void) -{ - int err; - int i, op; - uint64_t tmp; - - printk(KERN_INFO "\n"); - printk(KERN_INFO "=================================================\n"); - - if (dev < 0) { - pr_info("Please specify a valid mtd-device via module parameter\n"); - pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); - return -EINVAL; - } - - pr_info("MTD device: %d\n", dev); - - mtd = get_mtd_device(NULL, dev); - if (IS_ERR(mtd)) { - err = PTR_ERR(mtd); - pr_err("error: cannot get MTD device\n"); - return err; - } - - if (mtd->writesize == 1) { - pr_info("not NAND flash, assume page size is 512 " - "bytes.\n"); - pgsize = 512; - } else - pgsize = mtd->writesize; - - tmp = mtd->size; - do_div(tmp, mtd->erasesize); - ebcnt = tmp; - pgcnt = mtd->erasesize / pgsize; - - pr_info("MTD device size %llu, eraseblock size %u, " - "page size %u, count of eraseblocks %u, pages per " - "eraseblock %u, OOB size %u\n", - (unsigned long long)mtd->size, mtd->erasesize, - pgsize, ebcnt, pgcnt, mtd->oobsize); - - if (ebcnt < 2) { - pr_err("error: need at least 2 eraseblocks\n"); - err = -ENOSPC; - goto out_put_mtd; - } - - /* Read or write up 2 eraseblocks at a time */ - bufsize = mtd->erasesize * 2; - - err = -ENOMEM; - readbuf = vmalloc(bufsize); - writebuf = vmalloc(bufsize); - offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL); - if (!readbuf || !writebuf || !offsets) - goto out; - for (i = 0; i < ebcnt; i++) - offsets[i] = mtd->erasesize; - prandom_bytes(writebuf, bufsize); - - err = scan_for_bad_eraseblocks(); - if (err) - goto out; - - /* Do operations */ - pr_info("doing operations\n"); - for (op = 0; op < count; op++) { - if ((op & 1023) == 0) - pr_info("%d operations done\n", op); - err = do_operation(); - if (err) - goto out; - cond_resched(); - } - pr_info("finished, %d operations done\n", op); - -out: - kfree(offsets); - kfree(bbt); - vfree(writebuf); - vfree(readbuf); -out_put_mtd: - put_mtd_device(mtd); - if (err) - pr_info("error %d occurred\n", err); - printk(KERN_INFO "=================================================\n"); - return err; -} -module_init(mtd_stresstest_init); - -static void __exit mtd_stresstest_exit(void) -{ - return; -} -module_exit(mtd_stresstest_exit); - -MODULE_DESCRIPTION("Stress test module"); -MODULE_AUTHOR("Adrian Hunter"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c deleted file mode 100644 index e41a04f..0000000 --- a/drivers/mtd/tests/mtd_subpagetest.c +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright (C) 2006-2007 Nokia Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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; see the file COPYING. If not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Test sub-page read and write on MTD device. - * Author: Adrian Hunter - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -static int dev = -EINVAL; -module_param(dev, int, S_IRUGO); -MODULE_PARM_DESC(dev, "MTD device number to use"); - -static struct mtd_info *mtd; -static unsigned char *writebuf; -static unsigned char *readbuf; -static unsigned char *bbt; - -static int subpgsize; -static int bufsize; -static int ebcnt; -static int pgcnt; -static int errcnt; -static struct rnd_state rnd_state; - -static inline void clear_data(unsigned char *buf, size_t len) -{ - memset(buf, 0, len); -} - -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -static int erase_whole_device(void) -{ - int err; - unsigned int i; - - pr_info("erasing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - return err; - cond_resched(); - } - pr_info("erased %u eraseblocks\n", i); - return 0; -} - -static int write_eraseblock(int ebnum) -{ - size_t written; - int err = 0; - loff_t addr = ebnum * mtd->erasesize; - - prandom_bytes_state(&rnd_state, writebuf, subpgsize); - err = mtd_write(mtd, addr, subpgsize, &written, writebuf); - if (unlikely(err || written != subpgsize)) { - pr_err("error: write failed at %#llx\n", - (long long)addr); - if (written != subpgsize) { - pr_err(" write size: %#x\n", subpgsize); - pr_err(" written: %#zx\n", written); - } - return err ? err : -1; - } - - addr += subpgsize; - - prandom_bytes_state(&rnd_state, writebuf, subpgsize); - err = mtd_write(mtd, addr, subpgsize, &written, writebuf); - if (unlikely(err || written != subpgsize)) { - pr_err("error: write failed at %#llx\n", - (long long)addr); - if (written != subpgsize) { - pr_err(" write size: %#x\n", subpgsize); - pr_err(" written: %#zx\n", written); - } - return err ? err : -1; - } - - return err; -} - -static int write_eraseblock2(int ebnum) -{ - size_t written; - int err = 0, k; - loff_t addr = ebnum * mtd->erasesize; - - for (k = 1; k < 33; ++k) { - if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) - break; - prandom_bytes_state(&rnd_state, writebuf, subpgsize * k); - err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf); - if (unlikely(err || written != subpgsize * k)) { - pr_err("error: write failed at %#llx\n", - (long long)addr); - if (written != subpgsize) { - pr_err(" write size: %#x\n", - subpgsize * k); - pr_err(" written: %#08zx\n", - written); - } - return err ? err : -1; - } - addr += subpgsize * k; - } - - return err; -} - -static void print_subpage(unsigned char *p) -{ - int i, j; - - for (i = 0; i < subpgsize; ) { - for (j = 0; i < subpgsize && j < 32; ++i, ++j) - printk("%02x", *p++); - printk("\n"); - } -} - -static int verify_eraseblock(int ebnum) -{ - size_t read; - int err = 0; - loff_t addr = ebnum * mtd->erasesize; - - prandom_bytes_state(&rnd_state, writebuf, subpgsize); - clear_data(readbuf, subpgsize); - err = mtd_read(mtd, addr, subpgsize, &read, readbuf); - if (unlikely(err || read != subpgsize)) { - if (mtd_is_bitflip(err) && read == subpgsize) { - pr_info("ECC correction at %#llx\n", - (long long)addr); - err = 0; - } else { - pr_err("error: read failed at %#llx\n", - (long long)addr); - return err ? err : -1; - } - } - if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { - pr_err("error: verify failed at %#llx\n", - (long long)addr); - pr_info("------------- written----------------\n"); - print_subpage(writebuf); - pr_info("------------- read ------------------\n"); - print_subpage(readbuf); - pr_info("-------------------------------------\n"); - errcnt += 1; - } - - addr += subpgsize; - - prandom_bytes_state(&rnd_state, writebuf, subpgsize); - clear_data(readbuf, subpgsize); - err = mtd_read(mtd, addr, subpgsize, &read, readbuf); - if (unlikely(err || read != subpgsize)) { - if (mtd_is_bitflip(err) && read == subpgsize) { - pr_info("ECC correction at %#llx\n", - (long long)addr); - err = 0; - } else { - pr_err("error: read failed at %#llx\n", - (long long)addr); - return err ? err : -1; - } - } - if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { - pr_info("error: verify failed at %#llx\n", - (long long)addr); - pr_info("------------- written----------------\n"); - print_subpage(writebuf); - pr_info("------------- read ------------------\n"); - print_subpage(readbuf); - pr_info("-------------------------------------\n"); - errcnt += 1; - } - - return err; -} - -static int verify_eraseblock2(int ebnum) -{ - size_t read; - int err = 0, k; - loff_t addr = ebnum * mtd->erasesize; - - for (k = 1; k < 33; ++k) { - if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) - break; - prandom_bytes_state(&rnd_state, writebuf, subpgsize * k); - clear_data(readbuf, subpgsize * k); - err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf); - if (unlikely(err || read != subpgsize * k)) { - if (mtd_is_bitflip(err) && read == subpgsize * k) { - pr_info("ECC correction at %#llx\n", - (long long)addr); - err = 0; - } else { - pr_err("error: read failed at " - "%#llx\n", (long long)addr); - return err ? err : -1; - } - } - if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) { - pr_err("error: verify failed at %#llx\n", - (long long)addr); - errcnt += 1; - } - addr += subpgsize * k; - } - - return err; -} - -static int verify_eraseblock_ff(int ebnum) -{ - uint32_t j; - size_t read; - int err = 0; - loff_t addr = ebnum * mtd->erasesize; - - memset(writebuf, 0xff, subpgsize); - for (j = 0; j < mtd->erasesize / subpgsize; ++j) { - clear_data(readbuf, subpgsize); - err = mtd_read(mtd, addr, subpgsize, &read, readbuf); - if (unlikely(err || read != subpgsize)) { - if (mtd_is_bitflip(err) && read == subpgsize) { - pr_info("ECC correction at %#llx\n", - (long long)addr); - err = 0; - } else { - pr_err("error: read failed at " - "%#llx\n", (long long)addr); - return err ? err : -1; - } - } - if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { - pr_err("error: verify 0xff failed at " - "%#llx\n", (long long)addr); - errcnt += 1; - } - addr += subpgsize; - } - - return err; -} - -static int verify_all_eraseblocks_ff(void) -{ - int err; - unsigned int i; - - pr_info("verifying all eraseblocks for 0xff\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = verify_eraseblock_ff(i); - if (err) - return err; - if (i % 256 == 0) - pr_info("verified up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("verified %u eraseblocks\n", i); - return 0; -} - -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - -static int __init mtd_subpagetest_init(void) -{ - int err = 0; - uint32_t i; - uint64_t tmp; - - printk(KERN_INFO "\n"); - printk(KERN_INFO "=================================================\n"); - - if (dev < 0) { - pr_info("Please specify a valid mtd-device via module parameter\n"); - pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); - return -EINVAL; - } - - pr_info("MTD device: %d\n", dev); - - mtd = get_mtd_device(NULL, dev); - if (IS_ERR(mtd)) { - err = PTR_ERR(mtd); - pr_err("error: cannot get MTD device\n"); - return err; - } - - if (mtd->type != MTD_NANDFLASH) { - pr_info("this test requires NAND flash\n"); - goto out; - } - - subpgsize = mtd->writesize >> mtd->subpage_sft; - tmp = mtd->size; - do_div(tmp, mtd->erasesize); - ebcnt = tmp; - pgcnt = mtd->erasesize / mtd->writesize; - - pr_info("MTD device size %llu, eraseblock size %u, " - "page size %u, subpage size %u, count of eraseblocks %u, " - "pages per eraseblock %u, OOB size %u\n", - (unsigned long long)mtd->size, mtd->erasesize, - mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize); - - err = -ENOMEM; - bufsize = subpgsize * 32; - writebuf = kmalloc(bufsize, GFP_KERNEL); - if (!writebuf) - goto out; - readbuf = kmalloc(bufsize, GFP_KERNEL); - if (!readbuf) - goto out; - - err = scan_for_bad_eraseblocks(); - if (err) - goto out; - - err = erase_whole_device(); - if (err) - goto out; - - pr_info("writing whole device\n"); - prandom_seed_state(&rnd_state, 1); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = write_eraseblock(i); - if (unlikely(err)) - goto out; - if (i % 256 == 0) - pr_info("written up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("written %u eraseblocks\n", i); - - prandom_seed_state(&rnd_state, 1); - pr_info("verifying all eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = verify_eraseblock(i); - if (unlikely(err)) - goto out; - if (i % 256 == 0) - pr_info("verified up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("verified %u eraseblocks\n", i); - - err = erase_whole_device(); - if (err) - goto out; - - err = verify_all_eraseblocks_ff(); - if (err) - goto out; - - /* Write all eraseblocks */ - prandom_seed_state(&rnd_state, 3); - pr_info("writing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = write_eraseblock2(i); - if (unlikely(err)) - goto out; - if (i % 256 == 0) - pr_info("written up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("written %u eraseblocks\n", i); - - /* Check all eraseblocks */ - prandom_seed_state(&rnd_state, 3); - pr_info("verifying all eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = verify_eraseblock2(i); - if (unlikely(err)) - goto out; - if (i % 256 == 0) - pr_info("verified up to eraseblock %u\n", i); - cond_resched(); - } - pr_info("verified %u eraseblocks\n", i); - - err = erase_whole_device(); - if (err) - goto out; - - err = verify_all_eraseblocks_ff(); - if (err) - goto out; - - pr_info("finished with %d errors\n", errcnt); - -out: - kfree(bbt); - kfree(readbuf); - kfree(writebuf); - put_mtd_device(mtd); - if (err) - pr_info("error %d occurred\n", err); - printk(KERN_INFO "=================================================\n"); - return err; -} -module_init(mtd_subpagetest_init); - -static void __exit mtd_subpagetest_exit(void) -{ - return; -} -module_exit(mtd_subpagetest_exit); - -MODULE_DESCRIPTION("Subpage test module"); -MODULE_AUTHOR("Adrian Hunter"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c deleted file mode 100644 index 3a9f6a6..0000000 --- a/drivers/mtd/tests/mtd_torturetest.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (C) 2006-2008 Artem Bityutskiy - * Copyright (C) 2006-2008 Jarkko Lavinen - * Copyright (C) 2006-2008 Adrian Hunter - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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; see the file COPYING. If not, write to the Free Software - * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter - * - * WARNING: this test program may kill your flash and your device. Do not - * use it unless you know what you do. Authors are not responsible for any - * damage caused by this program. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#define RETRIES 3 - -static int eb = 8; -module_param(eb, int, S_IRUGO); -MODULE_PARM_DESC(eb, "eraseblock number within the selected MTD device"); - -static int ebcnt = 32; -module_param(ebcnt, int, S_IRUGO); -MODULE_PARM_DESC(ebcnt, "number of consecutive eraseblocks to torture"); - -static int pgcnt; -module_param(pgcnt, int, S_IRUGO); -MODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)"); - -static int dev = -EINVAL; -module_param(dev, int, S_IRUGO); -MODULE_PARM_DESC(dev, "MTD device number to use"); - -static int gran = 512; -module_param(gran, int, S_IRUGO); -MODULE_PARM_DESC(gran, "how often the status information should be printed"); - -static int check = 1; -module_param(check, int, S_IRUGO); -MODULE_PARM_DESC(check, "if the written data should be checked"); - -static unsigned int cycles_count; -module_param(cycles_count, uint, S_IRUGO); -MODULE_PARM_DESC(cycles_count, "how many erase cycles to do " - "(infinite by default)"); - -static struct mtd_info *mtd; - -/* This buffer contains 0x555555...0xAAAAAA... pattern */ -static unsigned char *patt_5A5; -/* This buffer contains 0xAAAAAA...0x555555... pattern */ -static unsigned char *patt_A5A; -/* This buffer contains all 0xFF bytes */ -static unsigned char *patt_FF; -/* This a temporary buffer is use when checking data */ -static unsigned char *check_buf; -/* How many erase cycles were done */ -static unsigned int erase_cycles; - -static int pgsize; -static struct timeval start, finish; - -static void report_corrupt(unsigned char *read, unsigned char *written); - -static inline void start_timing(void) -{ - do_gettimeofday(&start); -} - -static inline void stop_timing(void) -{ - do_gettimeofday(&finish); -} - -/* - * Erase eraseblock number @ebnum. - */ -static inline int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -/* - * Check that the contents of eraseblock number @enbum is equivalent to the - * @buf buffer. - */ -static inline int check_eraseblock(int ebnum, unsigned char *buf) -{ - int err, retries = 0; - size_t read; - loff_t addr = ebnum * mtd->erasesize; - size_t len = mtd->erasesize; - - if (pgcnt) { - addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize; - len = pgcnt * pgsize; - } - -retry: - err = mtd_read(mtd, addr, len, &read, check_buf); - if (mtd_is_bitflip(err)) - pr_err("single bit flip occurred at EB %d " - "MTD reported that it was fixed.\n", ebnum); - else if (err) { - pr_err("error %d while reading EB %d, " - "read %zd\n", err, ebnum, read); - return err; - } - - if (read != len) { - pr_err("failed to read %zd bytes from EB %d, " - "read only %zd, but no error reported\n", - len, ebnum, read); - return -EIO; - } - - if (memcmp(buf, check_buf, len)) { - pr_err("read wrong data from EB %d\n", ebnum); - report_corrupt(check_buf, buf); - - if (retries++ < RETRIES) { - /* Try read again */ - yield(); - pr_info("re-try reading data from EB %d\n", - ebnum); - goto retry; - } else { - pr_info("retried %d times, still errors, " - "give-up\n", RETRIES); - return -EINVAL; - } - } - - if (retries != 0) - pr_info("only attempt number %d was OK (!!!)\n", - retries); - - return 0; -} - -static inline int write_pattern(int ebnum, void *buf) -{ - int err; - size_t written; - loff_t addr = ebnum * mtd->erasesize; - size_t len = mtd->erasesize; - - if (pgcnt) { - addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize; - len = pgcnt * pgsize; - } - err = mtd_write(mtd, addr, len, &written, buf); - if (err) { - pr_err("error %d while writing EB %d, written %zd" - " bytes\n", err, ebnum, written); - return err; - } - if (written != len) { - pr_info("written only %zd bytes of %zd, but no error" - " reported\n", written, len); - return -EIO; - } - - return 0; -} - -static int __init tort_init(void) -{ - int err = 0, i, infinite = !cycles_count; - int *bad_ebs; - - printk(KERN_INFO "\n"); - printk(KERN_INFO "=================================================\n"); - pr_info("Warning: this program is trying to wear out your " - "flash, stop it if this is not wanted.\n"); - - if (dev < 0) { - pr_info("Please specify a valid mtd-device via module parameter\n"); - pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); - return -EINVAL; - } - - pr_info("MTD device: %d\n", dev); - pr_info("torture %d eraseblocks (%d-%d) of mtd%d\n", - ebcnt, eb, eb + ebcnt - 1, dev); - if (pgcnt) - pr_info("torturing just %d pages per eraseblock\n", - pgcnt); - pr_info("write verify %s\n", check ? "enabled" : "disabled"); - - mtd = get_mtd_device(NULL, dev); - if (IS_ERR(mtd)) { - err = PTR_ERR(mtd); - pr_err("error: cannot get MTD device\n"); - return err; - } - - if (mtd->writesize == 1) { - pr_info("not NAND flash, assume page size is 512 " - "bytes.\n"); - pgsize = 512; - } else - pgsize = mtd->writesize; - - if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) { - pr_err("error: invalid pgcnt value %d\n", pgcnt); - goto out_mtd; - } - - err = -ENOMEM; - patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!patt_5A5) - goto out_mtd; - - patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!patt_A5A) - goto out_patt_5A5; - - patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!patt_FF) - goto out_patt_A5A; - - check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!check_buf) - goto out_patt_FF; - - bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL); - if (!bad_ebs) - goto out_check_buf; - - err = 0; - - /* Initialize patterns */ - memset(patt_FF, 0xFF, mtd->erasesize); - for (i = 0; i < mtd->erasesize / pgsize; i++) { - if (!(i & 1)) { - memset(patt_5A5 + i * pgsize, 0x55, pgsize); - memset(patt_A5A + i * pgsize, 0xAA, pgsize); - } else { - memset(patt_5A5 + i * pgsize, 0xAA, pgsize); - memset(patt_A5A + i * pgsize, 0x55, pgsize); - } - } - - /* - * Check if there is a bad eraseblock among those we are going to test. - */ - if (mtd_can_have_bb(mtd)) { - for (i = eb; i < eb + ebcnt; i++) { - err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); - - if (err < 0) { - pr_info("block_isbad() returned %d " - "for EB %d\n", err, i); - goto out; - } - - if (err) { - pr_err("EB %d is bad. Skip it.\n", i); - bad_ebs[i - eb] = 1; - } - } - } - - start_timing(); - while (1) { - int i; - void *patt; - - /* Erase all eraseblocks */ - for (i = eb; i < eb + ebcnt; i++) { - if (bad_ebs[i - eb]) - continue; - err = erase_eraseblock(i); - if (err) - goto out; - cond_resched(); - } - - /* Check if the eraseblocks contain only 0xFF bytes */ - if (check) { - for (i = eb; i < eb + ebcnt; i++) { - if (bad_ebs[i - eb]) - continue; - err = check_eraseblock(i, patt_FF); - if (err) { - pr_info("verify failed" - " for 0xFF... pattern\n"); - goto out; - } - cond_resched(); - } - } - - /* Write the pattern */ - for (i = eb; i < eb + ebcnt; i++) { - if (bad_ebs[i - eb]) - continue; - if ((eb + erase_cycles) & 1) - patt = patt_5A5; - else - patt = patt_A5A; - err = write_pattern(i, patt); - if (err) - goto out; - cond_resched(); - } - - /* Verify what we wrote */ - if (check) { - for (i = eb; i < eb + ebcnt; i++) { - if (bad_ebs[i - eb]) - continue; - if ((eb + erase_cycles) & 1) - patt = patt_5A5; - else - patt = patt_A5A; - err = check_eraseblock(i, patt); - if (err) { - pr_info("verify failed for %s" - " pattern\n", - ((eb + erase_cycles) & 1) ? - "0x55AA55..." : "0xAA55AA..."); - goto out; - } - cond_resched(); - } - } - - erase_cycles += 1; - - if (erase_cycles % gran == 0) { - long ms; - - stop_timing(); - ms = (finish.tv_sec - start.tv_sec) * 1000 + - (finish.tv_usec - start.tv_usec) / 1000; - pr_info("%08u erase cycles done, took %lu " - "milliseconds (%lu seconds)\n", - erase_cycles, ms, ms / 1000); - start_timing(); - } - - if (!infinite && --cycles_count == 0) - break; - } -out: - - pr_info("finished after %u erase cycles\n", - erase_cycles); - kfree(bad_ebs); -out_check_buf: - kfree(check_buf); -out_patt_FF: - kfree(patt_FF); -out_patt_A5A: - kfree(patt_A5A); -out_patt_5A5: - kfree(patt_5A5); -out_mtd: - put_mtd_device(mtd); - if (err) - pr_info("error %d occurred during torturing\n", err); - printk(KERN_INFO "=================================================\n"); - return err; -} -module_init(tort_init); - -static void __exit tort_exit(void) -{ - return; -} -module_exit(tort_exit); - -static int countdiffs(unsigned char *buf, unsigned char *check_buf, - unsigned offset, unsigned len, unsigned *bytesp, - unsigned *bitsp); -static void print_bufs(unsigned char *read, unsigned char *written, int start, - int len); - -/* - * Report the detailed information about how the read EB differs from what was - * written. - */ -static void report_corrupt(unsigned char *read, unsigned char *written) -{ - int i; - int bytes, bits, pages, first; - int offset, len; - size_t check_len = mtd->erasesize; - - if (pgcnt) - check_len = pgcnt * pgsize; - - bytes = bits = pages = 0; - for (i = 0; i < check_len; i += pgsize) - if (countdiffs(written, read, i, pgsize, &bytes, - &bits) >= 0) - pages++; - - pr_info("verify fails on %d pages, %d bytes/%d bits\n", - pages, bytes, bits); - pr_info("The following is a list of all differences between" - " what was read from flash and what was expected\n"); - - for (i = 0; i < check_len; i += pgsize) { - cond_resched(); - bytes = bits = 0; - first = countdiffs(written, read, i, pgsize, &bytes, - &bits); - if (first < 0) - continue; - - printk("-------------------------------------------------------" - "----------------------------------\n"); - - pr_info("Page %zd has %d bytes/%d bits failing verify," - " starting at offset 0x%x\n", - (mtd->erasesize - check_len + i) / pgsize, - bytes, bits, first); - - offset = first & ~0x7; - len = ((first + bytes) | 0x7) + 1 - offset; - - print_bufs(read, written, offset, len); - } -} - -static void print_bufs(unsigned char *read, unsigned char *written, int start, - int len) -{ - int i = 0, j1, j2; - char *diff; - - printk("Offset Read Written\n"); - while (i < len) { - printk("0x%08x: ", start + i); - diff = " "; - for (j1 = 0; j1 < 8 && i + j1 < len; j1++) { - printk(" %02x", read[start + i + j1]); - if (read[start + i + j1] != written[start + i + j1]) - diff = "***"; - } - - while (j1 < 8) { - printk(" "); - j1 += 1; - } - - printk(" %s ", diff); - - for (j2 = 0; j2 < 8 && i + j2 < len; j2++) - printk(" %02x", written[start + i + j2]); - printk("\n"); - i += 8; - } -} - -/* - * Count the number of differing bytes and bits and return the first differing - * offset. - */ -static int countdiffs(unsigned char *buf, unsigned char *check_buf, - unsigned offset, unsigned len, unsigned *bytesp, - unsigned *bitsp) -{ - unsigned i, bit; - int first = -1; - - for (i = offset; i < offset + len; i++) - if (buf[i] != check_buf[i]) { - first = i; - break; - } - - while (i < offset + len) { - if (buf[i] != check_buf[i]) { - (*bytesp)++; - bit = 1; - while (bit < 256) { - if ((buf[i] & bit) != (check_buf[i] & bit)) - (*bitsp)++; - bit <<= 1; - } - } - i++; - } - - return first; -} - -MODULE_DESCRIPTION("Eraseblock torturing module"); -MODULE_AUTHOR("Artem Bityutskiy, Jarkko Lavinen, Adrian Hunter"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/nandbiterrs.c b/drivers/mtd/tests/nandbiterrs.c new file mode 100644 index 0000000..207bf9a --- /dev/null +++ b/drivers/mtd/tests/nandbiterrs.c @@ -0,0 +1,461 @@ +/* + * Copyright © 2012 NetCommWireless + * Iwo Mergler + * + * Test for multi-bit error recovery on a NAND page This mostly tests the + * ECC controller / driver. + * + * There are two test modes: + * + * 0 - artificially inserting bit errors until the ECC fails + * This is the default method and fairly quick. It should + * be independent of the quality of the FLASH. + * + * 1 - re-writing the same pattern repeatedly until the ECC fails. + * This method relies on the physics of NAND FLASH to eventually + * generate '0' bits if '1' has been written sufficient times. + * Depending on the NAND, the first bit errors will appear after + * 1000 or more writes and then will usually snowball, reaching the + * limits of the ECC quickly. + * + * The test stops after 10000 cycles, should your FLASH be + * exceptionally good and not generate bit errors before that. Try + * a different page in that case. + * + * Please note that neither of these tests will significantly 'use up' any + * FLASH endurance. Only a maximum of two erase operations will be performed. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +static int dev; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static unsigned page_offset; +module_param(page_offset, uint, S_IRUGO); +MODULE_PARM_DESC(page_offset, "Page number relative to dev start"); + +static unsigned seed; +module_param(seed, uint, S_IRUGO); +MODULE_PARM_DESC(seed, "Random seed"); + +static int mode; +module_param(mode, int, S_IRUGO); +MODULE_PARM_DESC(mode, "0=incremental errors, 1=overwrite test"); + +static unsigned max_overwrite = 10000; + +static loff_t offset; /* Offset of the page we're using. */ +static unsigned eraseblock; /* Eraseblock number for our page. */ + +/* We assume that the ECC can correct up to a certain number + * of biterrors per subpage. */ +static unsigned subsize; /* Size of subpages */ +static unsigned subcount; /* Number of subpages per page */ + +static struct mtd_info *mtd; /* MTD device */ + +static uint8_t *wbuffer; /* One page write / compare buffer */ +static uint8_t *rbuffer; /* One page read buffer */ + +/* 'random' bytes from known offsets */ +static uint8_t hash(unsigned offset) +{ + unsigned v = offset; + unsigned char c; + v ^= 0x7f7edfd3; + v = v ^ (v >> 3); + v = v ^ (v >> 5); + v = v ^ (v >> 13); + c = v & 0xFF; + /* Reverse bits of result. */ + c = (c & 0x0F) << 4 | (c & 0xF0) >> 4; + c = (c & 0x33) << 2 | (c & 0xCC) >> 2; + c = (c & 0x55) << 1 | (c & 0xAA) >> 1; + return c; +} + +static int erase_block(void) +{ + int err; + struct erase_info ei; + loff_t addr = eraseblock * mtd->erasesize; + + pr_info("erase_block\n"); + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd_erase(mtd, &ei); + if (err || ei.state == MTD_ERASE_FAILED) { + pr_err("error %d while erasing\n", err); + if (!err) + err = -EIO; + return err; + } + + return 0; +} + +/* Writes wbuffer to page */ +static int write_page(int log) +{ + int err = 0; + size_t written; + + if (log) + pr_info("write_page\n"); + + err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer); + if (err || written != mtd->writesize) { + pr_err("error: write failed at %#llx\n", (long long)offset); + if (!err) + err = -EIO; + } + + return err; +} + +/* Re-writes the data area while leaving the OOB alone. */ +static int rewrite_page(int log) +{ + int err = 0; + struct mtd_oob_ops ops; + + if (log) + pr_info("rewrite page\n"); + + ops.mode = MTD_OPS_RAW; /* No ECC */ + ops.len = mtd->writesize; + ops.retlen = 0; + ops.ooblen = 0; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = wbuffer; + ops.oobbuf = NULL; + + err = mtd_write_oob(mtd, offset, &ops); + if (err || ops.retlen != mtd->writesize) { + pr_err("error: write_oob failed (%d)\n", err); + if (!err) + err = -EIO; + } + + return err; +} + +/* Reads page into rbuffer. Returns number of corrected bit errors (>=0) + * or error (<0) */ +static int read_page(int log) +{ + int err = 0; + size_t read; + struct mtd_ecc_stats oldstats; + + if (log) + pr_info("read_page\n"); + + /* Saving last mtd stats */ + memcpy(&oldstats, &mtd->ecc_stats, sizeof(oldstats)); + + err = mtd_read(mtd, offset, mtd->writesize, &read, rbuffer); + if (err == -EUCLEAN) + err = mtd->ecc_stats.corrected - oldstats.corrected; + + if (err < 0 || read != mtd->writesize) { + pr_err("error: read failed at %#llx\n", (long long)offset); + if (err >= 0) + err = -EIO; + } + + return err; +} + +/* Verifies rbuffer against random sequence */ +static int verify_page(int log) +{ + unsigned i, errs = 0; + + if (log) + pr_info("verify_page\n"); + + for (i = 0; i < mtd->writesize; i++) { + if (rbuffer[i] != hash(i+seed)) { + pr_err("Error: page offset %u, expected %02x, got %02x\n", + i, hash(i+seed), rbuffer[i]); + errs++; + } + } + + if (errs) + return -EIO; + else + return 0; +} + +#define CBIT(v, n) ((v) & (1 << (n))) +#define BCLR(v, n) ((v) = (v) & ~(1 << (n))) + +/* Finds the first '1' bit in wbuffer starting at offset 'byte' + * and sets it to '0'. */ +static int insert_biterror(unsigned byte) +{ + int bit; + + while (byte < mtd->writesize) { + for (bit = 7; bit >= 0; bit--) { + if (CBIT(wbuffer[byte], bit)) { + BCLR(wbuffer[byte], bit); + pr_info("Inserted biterror @ %u/%u\n", byte, bit); + return 0; + } + } + byte++; + } + pr_err("biterror: Failed to find a '1' bit\n"); + return -EIO; +} + +/* Writes 'random' data to page and then introduces deliberate bit + * errors into the page, while verifying each step. */ +static int incremental_errors_test(void) +{ + int err = 0; + unsigned i; + unsigned errs_per_subpage = 0; + + pr_info("incremental biterrors test\n"); + + for (i = 0; i < mtd->writesize; i++) + wbuffer[i] = hash(i+seed); + + err = write_page(1); + if (err) + goto exit; + + while (1) { + + err = rewrite_page(1); + if (err) + goto exit; + + err = read_page(1); + if (err > 0) + pr_info("Read reported %d corrected bit errors\n", err); + if (err < 0) { + pr_err("After %d biterrors per subpage, read reported error %d\n", + errs_per_subpage, err); + err = 0; + goto exit; + } + + err = verify_page(1); + if (err) { + pr_err("ECC failure, read data is incorrect despite read success\n"); + goto exit; + } + + pr_info("Successfully corrected %d bit errors per subpage\n", + errs_per_subpage); + + for (i = 0; i < subcount; i++) { + err = insert_biterror(i * subsize); + if (err < 0) + goto exit; + } + errs_per_subpage++; + } + +exit: + return err; +} + + +/* Writes 'random' data to page and then re-writes that same data repeatedly. + This eventually develops bit errors (bits written as '1' will slowly become + '0'), which are corrected as far as the ECC is capable of. */ +static int overwrite_test(void) +{ + int err = 0; + unsigned i; + unsigned max_corrected = 0; + unsigned opno = 0; + /* We don't expect more than this many correctable bit errors per + * page. */ + #define MAXBITS 512 + static unsigned bitstats[MAXBITS]; /* bit error histogram. */ + + memset(bitstats, 0, sizeof(bitstats)); + + pr_info("overwrite biterrors test\n"); + + for (i = 0; i < mtd->writesize; i++) + wbuffer[i] = hash(i+seed); + + err = write_page(1); + if (err) + goto exit; + + while (opno < max_overwrite) { + + err = rewrite_page(0); + if (err) + break; + + err = read_page(0); + if (err >= 0) { + if (err >= MAXBITS) { + pr_info("Implausible number of bit errors corrected\n"); + err = -EIO; + break; + } + bitstats[err]++; + if (err > max_corrected) { + max_corrected = err; + pr_info("Read reported %d corrected bit errors\n", + err); + } + } else { /* err < 0 */ + pr_info("Read reported error %d\n", err); + err = 0; + break; + } + + err = verify_page(0); + if (err) { + bitstats[max_corrected] = opno; + pr_info("ECC failure, read data is incorrect despite read success\n"); + break; + } + + opno++; + } + + /* At this point bitstats[0] contains the number of ops with no bit + * errors, bitstats[1] the number of ops with 1 bit error, etc. */ + pr_info("Bit error histogram (%d operations total):\n", opno); + for (i = 0; i < max_corrected; i++) + pr_info("Page reads with %3d corrected bit errors: %d\n", + i, bitstats[i]); + +exit: + return err; +} + +static int __init mtd_nandbiterrs_init(void) +{ + int err = 0; + + printk("\n"); + printk(KERN_INFO "==================================================\n"); + pr_info("MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_err("error: cannot get MTD device\n"); + goto exit_mtddev; + } + + if (mtd->type != MTD_NANDFLASH) { + pr_info("this test requires NAND flash\n"); + err = -ENODEV; + goto exit_nand; + } + + pr_info("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n", + (unsigned long long)mtd->size, mtd->erasesize, + mtd->writesize, mtd->oobsize); + + subsize = mtd->writesize >> mtd->subpage_sft; + subcount = mtd->writesize / subsize; + + pr_info("Device uses %d subpages of %d bytes\n", subcount, subsize); + + offset = page_offset * mtd->writesize; + eraseblock = mtd_div_by_eb(offset, mtd); + + pr_info("Using page=%u, offset=%llu, eraseblock=%u\n", + page_offset, offset, eraseblock); + + wbuffer = kmalloc(mtd->writesize, GFP_KERNEL); + if (!wbuffer) { + err = -ENOMEM; + goto exit_wbuffer; + } + + rbuffer = kmalloc(mtd->writesize, GFP_KERNEL); + if (!rbuffer) { + err = -ENOMEM; + goto exit_rbuffer; + } + + err = erase_block(); + if (err) + goto exit_error; + + if (mode == 0) + err = incremental_errors_test(); + else + err = overwrite_test(); + + if (err) + goto exit_error; + + /* We leave the block un-erased in case of test failure. */ + err = erase_block(); + if (err) + goto exit_error; + + err = -EIO; + pr_info("finished successfully.\n"); + printk(KERN_INFO "==================================================\n"); + +exit_error: + kfree(rbuffer); +exit_rbuffer: + kfree(wbuffer); +exit_wbuffer: + /* Nothing */ +exit_nand: + put_mtd_device(mtd); +exit_mtddev: + return err; +} + +static void __exit mtd_nandbiterrs_exit(void) +{ + return; +} + +module_init(mtd_nandbiterrs_init); +module_exit(mtd_nandbiterrs_exit); + +MODULE_DESCRIPTION("NAND bit error recovery test"); +MODULE_AUTHOR("Iwo Mergler"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c new file mode 100644 index 0000000..ab81e9a --- /dev/null +++ b/drivers/mtd/tests/oobtest.c @@ -0,0 +1,714 @@ +/* + * Copyright (C) 2006-2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test OOB read and write on MTD device. + * + * Author: Adrian Hunter + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int dev = -EINVAL; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static struct mtd_info *mtd; +static unsigned char *readbuf; +static unsigned char *writebuf; +static unsigned char *bbt; + +static int ebcnt; +static int pgcnt; +static int errcnt; +static int use_offset; +static int use_len; +static int use_len_max; +static int vary_offset; +static struct rnd_state rnd_state; + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd_erase(mtd, &ei); + if (err) { + pr_err("error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + pr_err("some erase error occurred at EB %d\n", ebnum); + return -EIO; + } + + return 0; +} + +static int erase_whole_device(void) +{ + int err; + unsigned int i; + + pr_info("erasing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + return err; + cond_resched(); + } + pr_info("erased %u eraseblocks\n", i); + return 0; +} + +static void do_vary_offset(void) +{ + use_len -= 1; + if (use_len < 1) { + use_offset += 1; + if (use_offset >= use_len_max) + use_offset = 0; + use_len = use_len_max - use_offset; + } +} + +static int write_eraseblock(int ebnum) +{ + int i; + struct mtd_oob_ops ops; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { + prandom_bytes_state(&rnd_state, writebuf, use_len); + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = use_len; + ops.oobretlen = 0; + ops.ooboffs = use_offset; + ops.datbuf = NULL; + ops.oobbuf = writebuf; + err = mtd_write_oob(mtd, addr, &ops); + if (err || ops.oobretlen != use_len) { + pr_err("error: writeoob failed at %#llx\n", + (long long)addr); + pr_err("error: use_len %d, use_offset %d\n", + use_len, use_offset); + errcnt += 1; + return err ? err : -1; + } + if (vary_offset) + do_vary_offset(); + } + + return err; +} + +static int write_whole_device(void) +{ + int err; + unsigned int i; + + pr_info("writing OOBs of whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock(i); + if (err) + return err; + if (i % 256 == 0) + pr_info("written up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("written %u eraseblocks\n", i); + return 0; +} + +static int verify_eraseblock(int ebnum) +{ + int i; + struct mtd_oob_ops ops; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { + prandom_bytes_state(&rnd_state, writebuf, use_len); + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = use_len; + ops.oobretlen = 0; + ops.ooboffs = use_offset; + ops.datbuf = NULL; + ops.oobbuf = readbuf; + err = mtd_read_oob(mtd, addr, &ops); + if (err || ops.oobretlen != use_len) { + pr_err("error: readoob failed at %#llx\n", + (long long)addr); + errcnt += 1; + return err ? err : -1; + } + if (memcmp(readbuf, writebuf, use_len)) { + pr_err("error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + pr_err("error: too many errors\n"); + return -1; + } + } + if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) { + int k; + + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.oobbuf = readbuf; + err = mtd_read_oob(mtd, addr, &ops); + if (err || ops.oobretlen != mtd->ecclayout->oobavail) { + pr_err("error: readoob failed at %#llx\n", + (long long)addr); + errcnt += 1; + return err ? err : -1; + } + if (memcmp(readbuf + use_offset, writebuf, use_len)) { + pr_err("error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + pr_err("error: too many errors\n"); + return -1; + } + } + for (k = 0; k < use_offset; ++k) + if (readbuf[k] != 0xff) { + pr_err("error: verify 0xff " + "failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + pr_err("error: too " + "many errors\n"); + return -1; + } + } + for (k = use_offset + use_len; + k < mtd->ecclayout->oobavail; ++k) + if (readbuf[k] != 0xff) { + pr_err("error: verify 0xff " + "failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + pr_err("error: too " + "many errors\n"); + return -1; + } + } + } + if (vary_offset) + do_vary_offset(); + } + return err; +} + +static int verify_eraseblock_in_one_go(int ebnum) +{ + struct mtd_oob_ops ops; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + size_t len = mtd->ecclayout->oobavail * pgcnt; + + prandom_bytes_state(&rnd_state, writebuf, len); + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = len; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.oobbuf = readbuf; + err = mtd_read_oob(mtd, addr, &ops); + if (err || ops.oobretlen != len) { + pr_err("error: readoob failed at %#llx\n", + (long long)addr); + errcnt += 1; + return err ? err : -1; + } + if (memcmp(readbuf, writebuf, len)) { + pr_err("error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + pr_err("error: too many errors\n"); + return -1; + } + } + + return err; +} + +static int verify_all_eraseblocks(void) +{ + int err; + unsigned int i; + + pr_info("verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock(i); + if (err) + return err; + if (i % 256 == 0) + pr_info("verified up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("verified %u eraseblocks\n", i); + return 0; +} + +static int is_block_bad(int ebnum) +{ + int ret; + loff_t addr = ebnum * mtd->erasesize; + + ret = mtd_block_isbad(mtd, addr); + if (ret) + pr_info("block %d is bad\n", ebnum); + return ret; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kmalloc(ebcnt, GFP_KERNEL); + if (!bbt) + return -ENOMEM; + + pr_info("scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_oobtest_init(void) +{ + int err = 0; + unsigned int i; + uint64_t tmp; + struct mtd_oob_ops ops; + loff_t addr = 0, addr0; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + + if (dev < 0) { + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); + return -EINVAL; + } + + pr_info("MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_err("error: cannot get MTD device\n"); + return err; + } + + if (mtd->type != MTD_NANDFLASH) { + pr_info("this test requires NAND flash\n"); + goto out; + } + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + + pr_info("MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + mtd->writesize, ebcnt, pgcnt, mtd->oobsize); + + err = -ENOMEM; + readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!readbuf) + goto out; + writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!writebuf) + goto out; + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + use_offset = 0; + use_len = mtd->ecclayout->oobavail; + use_len_max = mtd->ecclayout->oobavail; + vary_offset = 0; + + /* First test: write all OOB, read it back and verify */ + pr_info("test 1 of 5\n"); + + err = erase_whole_device(); + if (err) + goto out; + + prandom_seed_state(&rnd_state, 1); + err = write_whole_device(); + if (err) + goto out; + + prandom_seed_state(&rnd_state, 1); + err = verify_all_eraseblocks(); + if (err) + goto out; + + /* + * Second test: write all OOB, a block at a time, read it back and + * verify. + */ + pr_info("test 2 of 5\n"); + + err = erase_whole_device(); + if (err) + goto out; + + prandom_seed_state(&rnd_state, 3); + err = write_whole_device(); + if (err) + goto out; + + /* Check all eraseblocks */ + prandom_seed_state(&rnd_state, 3); + pr_info("verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock_in_one_go(i); + if (err) + goto out; + if (i % 256 == 0) + pr_info("verified up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("verified %u eraseblocks\n", i); + + /* + * Third test: write OOB at varying offsets and lengths, read it back + * and verify. + */ + pr_info("test 3 of 5\n"); + + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks */ + use_offset = 0; + use_len = mtd->ecclayout->oobavail; + use_len_max = mtd->ecclayout->oobavail; + vary_offset = 1; + prandom_seed_state(&rnd_state, 5); + + err = write_whole_device(); + if (err) + goto out; + + /* Check all eraseblocks */ + use_offset = 0; + use_len = mtd->ecclayout->oobavail; + use_len_max = mtd->ecclayout->oobavail; + vary_offset = 1; + prandom_seed_state(&rnd_state, 5); + err = verify_all_eraseblocks(); + if (err) + goto out; + + use_offset = 0; + use_len = mtd->ecclayout->oobavail; + use_len_max = mtd->ecclayout->oobavail; + vary_offset = 0; + + /* Fourth test: try to write off end of device */ + pr_info("test 4 of 5\n"); + + err = erase_whole_device(); + if (err) + goto out; + + addr0 = 0; + for (i = 0; i < ebcnt && bbt[i]; ++i) + addr0 += mtd->erasesize; + + /* Attempt to write off end of OOB */ + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = 1; + ops.oobretlen = 0; + ops.ooboffs = mtd->ecclayout->oobavail; + ops.datbuf = NULL; + ops.oobbuf = writebuf; + pr_info("attempting to start write past end of OOB\n"); + pr_info("an error is expected...\n"); + err = mtd_write_oob(mtd, addr0, &ops); + if (err) { + pr_info("error occurred as expected\n"); + err = 0; + } else { + pr_err("error: can write past end of OOB\n"); + errcnt += 1; + } + + /* Attempt to read off end of OOB */ + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = 1; + ops.oobretlen = 0; + ops.ooboffs = mtd->ecclayout->oobavail; + ops.datbuf = NULL; + ops.oobbuf = readbuf; + pr_info("attempting to start read past end of OOB\n"); + pr_info("an error is expected...\n"); + err = mtd_read_oob(mtd, addr0, &ops); + if (err) { + pr_info("error occurred as expected\n"); + err = 0; + } else { + pr_err("error: can read past end of OOB\n"); + errcnt += 1; + } + + if (bbt[ebcnt - 1]) + pr_info("skipping end of device tests because last " + "block is bad\n"); + else { + /* Attempt to write off end of device */ + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail + 1; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.oobbuf = writebuf; + pr_info("attempting to write past end of device\n"); + pr_info("an error is expected...\n"); + err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); + if (err) { + pr_info("error occurred as expected\n"); + err = 0; + } else { + pr_err("error: wrote past end of device\n"); + errcnt += 1; + } + + /* Attempt to read off end of device */ + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail + 1; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.oobbuf = readbuf; + pr_info("attempting to read past end of device\n"); + pr_info("an error is expected...\n"); + err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); + if (err) { + pr_info("error occurred as expected\n"); + err = 0; + } else { + pr_err("error: read past end of device\n"); + errcnt += 1; + } + + err = erase_eraseblock(ebcnt - 1); + if (err) + goto out; + + /* Attempt to write off end of device */ + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail; + ops.oobretlen = 0; + ops.ooboffs = 1; + ops.datbuf = NULL; + ops.oobbuf = writebuf; + pr_info("attempting to write past end of device\n"); + pr_info("an error is expected...\n"); + err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); + if (err) { + pr_info("error occurred as expected\n"); + err = 0; + } else { + pr_err("error: wrote past end of device\n"); + errcnt += 1; + } + + /* Attempt to read off end of device */ + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail; + ops.oobretlen = 0; + ops.ooboffs = 1; + ops.datbuf = NULL; + ops.oobbuf = readbuf; + pr_info("attempting to read past end of device\n"); + pr_info("an error is expected...\n"); + err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); + if (err) { + pr_info("error occurred as expected\n"); + err = 0; + } else { + pr_err("error: read past end of device\n"); + errcnt += 1; + } + } + + /* Fifth test: write / read across block boundaries */ + pr_info("test 5 of 5\n"); + + /* Erase all eraseblocks */ + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks */ + prandom_seed_state(&rnd_state, 11); + pr_info("writing OOBs of whole device\n"); + for (i = 0; i < ebcnt - 1; ++i) { + int cnt = 2; + int pg; + size_t sz = mtd->ecclayout->oobavail; + if (bbt[i] || bbt[i + 1]) + continue; + addr = (i + 1) * mtd->erasesize - mtd->writesize; + for (pg = 0; pg < cnt; ++pg) { + prandom_bytes_state(&rnd_state, writebuf, sz); + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = sz; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.oobbuf = writebuf; + err = mtd_write_oob(mtd, addr, &ops); + if (err) + goto out; + if (i % 256 == 0) + pr_info("written up to eraseblock %u\n", i); + cond_resched(); + addr += mtd->writesize; + } + } + pr_info("written %u eraseblocks\n", i); + + /* Check all eraseblocks */ + prandom_seed_state(&rnd_state, 11); + pr_info("verifying all eraseblocks\n"); + for (i = 0; i < ebcnt - 1; ++i) { + if (bbt[i] || bbt[i + 1]) + continue; + prandom_bytes_state(&rnd_state, writebuf, + mtd->ecclayout->oobavail * 2); + addr = (i + 1) * mtd->erasesize - mtd->writesize; + ops.mode = MTD_OPS_AUTO_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail * 2; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.oobbuf = readbuf; + err = mtd_read_oob(mtd, addr, &ops); + if (err) + goto out; + if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) { + pr_err("error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + pr_err("error: too many errors\n"); + goto out; + } + } + if (i % 256 == 0) + pr_info("verified up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("verified %u eraseblocks\n", i); + + pr_info("finished with %d errors\n", errcnt); +out: + kfree(bbt); + kfree(writebuf); + kfree(readbuf); + put_mtd_device(mtd); + if (err) + pr_info("error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_oobtest_init); + +static void __exit mtd_oobtest_exit(void) +{ + return; +} +module_exit(mtd_oobtest_exit); + +MODULE_DESCRIPTION("Out-of-band test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c new file mode 100644 index 0000000..acd991f --- /dev/null +++ b/drivers/mtd/tests/pagetest.c @@ -0,0 +1,605 @@ +/* + * Copyright (C) 2006-2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test page read and write on MTD device. + * + * Author: Adrian Hunter + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int dev = -EINVAL; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static struct mtd_info *mtd; +static unsigned char *twopages; +static unsigned char *writebuf; +static unsigned char *boundary; +static unsigned char *bbt; + +static int pgsize; +static int bufsize; +static int ebcnt; +static int pgcnt; +static int errcnt; +static struct rnd_state rnd_state; + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd_erase(mtd, &ei); + if (err) { + pr_err("error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + pr_err("some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +static int write_eraseblock(int ebnum) +{ + int err = 0; + size_t written; + loff_t addr = ebnum * mtd->erasesize; + + prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); + cond_resched(); + err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf); + if (err || written != mtd->erasesize) + pr_err("error: write failed at %#llx\n", + (long long)addr); + + return err; +} + +static int verify_eraseblock(int ebnum) +{ + uint32_t j; + size_t read; + int err = 0, i; + loff_t addr0, addrn; + loff_t addr = ebnum * mtd->erasesize; + + addr0 = 0; + for (i = 0; i < ebcnt && bbt[i]; ++i) + addr0 += mtd->erasesize; + + addrn = mtd->size; + for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) + addrn -= mtd->erasesize; + + prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); + for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { + /* Do a read to set the internal dataRAMs to different data */ + err = mtd_read(mtd, addr0, bufsize, &read, twopages); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != bufsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr0); + return err; + } + err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != bufsize) { + pr_err("error: read failed at %#llx\n", + (long long)(addrn - bufsize)); + return err; + } + memset(twopages, 0, bufsize); + err = mtd_read(mtd, addr, bufsize, &read, twopages); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != bufsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr); + break; + } + if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) { + pr_err("error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + } + } + /* Check boundary between eraseblocks */ + if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) { + struct rnd_state old_state = rnd_state; + + /* Do a read to set the internal dataRAMs to different data */ + err = mtd_read(mtd, addr0, bufsize, &read, twopages); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != bufsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr0); + return err; + } + err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != bufsize) { + pr_err("error: read failed at %#llx\n", + (long long)(addrn - bufsize)); + return err; + } + memset(twopages, 0, bufsize); + err = mtd_read(mtd, addr, bufsize, &read, twopages); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != bufsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr); + return err; + } + memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); + prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize); + if (memcmp(twopages, boundary, bufsize)) { + pr_err("error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + } + rnd_state = old_state; + } + return err; +} + +static int crosstest(void) +{ + size_t read; + int err = 0, i; + loff_t addr, addr0, addrn; + unsigned char *pp1, *pp2, *pp3, *pp4; + + pr_info("crosstest\n"); + pp1 = kmalloc(pgsize * 4, GFP_KERNEL); + if (!pp1) + return -ENOMEM; + pp2 = pp1 + pgsize; + pp3 = pp2 + pgsize; + pp4 = pp3 + pgsize; + memset(pp1, 0, pgsize * 4); + + addr0 = 0; + for (i = 0; i < ebcnt && bbt[i]; ++i) + addr0 += mtd->erasesize; + + addrn = mtd->size; + for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) + addrn -= mtd->erasesize; + + /* Read 2nd-to-last page to pp1 */ + addr = addrn - pgsize - pgsize; + err = mtd_read(mtd, addr, pgsize, &read, pp1); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* Read 3rd-to-last page to pp1 */ + addr = addrn - pgsize - pgsize - pgsize; + err = mtd_read(mtd, addr, pgsize, &read, pp1); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* Read first page to pp2 */ + addr = addr0; + pr_info("reading page at %#llx\n", (long long)addr); + err = mtd_read(mtd, addr, pgsize, &read, pp2); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* Read last page to pp3 */ + addr = addrn - pgsize; + pr_info("reading page at %#llx\n", (long long)addr); + err = mtd_read(mtd, addr, pgsize, &read, pp3); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* Read first page again to pp4 */ + addr = addr0; + pr_info("reading page at %#llx\n", (long long)addr); + err = mtd_read(mtd, addr, pgsize, &read, pp4); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* pp2 and pp4 should be the same */ + pr_info("verifying pages read at %#llx match\n", + (long long)addr0); + if (memcmp(pp2, pp4, pgsize)) { + pr_err("verify failed!\n"); + errcnt += 1; + } else if (!err) + pr_info("crosstest ok\n"); + kfree(pp1); + return err; +} + +static int erasecrosstest(void) +{ + size_t read, written; + int err = 0, i, ebnum, ebnum2; + loff_t addr0; + char *readbuf = twopages; + + pr_info("erasecrosstest\n"); + + ebnum = 0; + addr0 = 0; + for (i = 0; i < ebcnt && bbt[i]; ++i) { + addr0 += mtd->erasesize; + ebnum += 1; + } + + ebnum2 = ebcnt - 1; + while (ebnum2 && bbt[ebnum2]) + ebnum2 -= 1; + + pr_info("erasing block %d\n", ebnum); + err = erase_eraseblock(ebnum); + if (err) + return err; + + pr_info("writing 1st page of block %d\n", ebnum); + prandom_bytes_state(&rnd_state, writebuf, pgsize); + strcpy(writebuf, "There is no data like this!"); + err = mtd_write(mtd, addr0, pgsize, &written, writebuf); + if (err || written != pgsize) { + pr_info("error: write failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + pr_info("reading 1st page of block %d\n", ebnum); + memset(readbuf, 0, pgsize); + err = mtd_read(mtd, addr0, pgsize, &read, readbuf); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + pr_info("verifying 1st page of block %d\n", ebnum); + if (memcmp(writebuf, readbuf, pgsize)) { + pr_err("verify failed!\n"); + errcnt += 1; + return -1; + } + + pr_info("erasing block %d\n", ebnum); + err = erase_eraseblock(ebnum); + if (err) + return err; + + pr_info("writing 1st page of block %d\n", ebnum); + prandom_bytes_state(&rnd_state, writebuf, pgsize); + strcpy(writebuf, "There is no data like this!"); + err = mtd_write(mtd, addr0, pgsize, &written, writebuf); + if (err || written != pgsize) { + pr_err("error: write failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + pr_info("erasing block %d\n", ebnum2); + err = erase_eraseblock(ebnum2); + if (err) + return err; + + pr_info("reading 1st page of block %d\n", ebnum); + memset(readbuf, 0, pgsize); + err = mtd_read(mtd, addr0, pgsize, &read, readbuf); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + pr_info("verifying 1st page of block %d\n", ebnum); + if (memcmp(writebuf, readbuf, pgsize)) { + pr_err("verify failed!\n"); + errcnt += 1; + return -1; + } + + if (!err) + pr_info("erasecrosstest ok\n"); + return err; +} + +static int erasetest(void) +{ + size_t read, written; + int err = 0, i, ebnum, ok = 1; + loff_t addr0; + + pr_info("erasetest\n"); + + ebnum = 0; + addr0 = 0; + for (i = 0; i < ebcnt && bbt[i]; ++i) { + addr0 += mtd->erasesize; + ebnum += 1; + } + + pr_info("erasing block %d\n", ebnum); + err = erase_eraseblock(ebnum); + if (err) + return err; + + pr_info("writing 1st page of block %d\n", ebnum); + prandom_bytes_state(&rnd_state, writebuf, pgsize); + err = mtd_write(mtd, addr0, pgsize, &written, writebuf); + if (err || written != pgsize) { + pr_err("error: write failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + pr_info("erasing block %d\n", ebnum); + err = erase_eraseblock(ebnum); + if (err) + return err; + + pr_info("reading 1st page of block %d\n", ebnum); + err = mtd_read(mtd, addr0, pgsize, &read, twopages); + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + pr_info("verifying 1st page of block %d is all 0xff\n", + ebnum); + for (i = 0; i < pgsize; ++i) + if (twopages[i] != 0xff) { + pr_err("verifying all 0xff failed at %d\n", + i); + errcnt += 1; + ok = 0; + break; + } + + if (ok && !err) + pr_info("erasetest ok\n"); + + return err; +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd_block_isbad(mtd, addr); + if (ret) + pr_info("block %d is bad\n", ebnum); + return ret; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + return -ENOMEM; + + pr_info("scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_pagetest_init(void) +{ + int err = 0; + uint64_t tmp; + uint32_t i; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + + if (dev < 0) { + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); + return -EINVAL; + } + + pr_info("MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_err("error: cannot get MTD device\n"); + return err; + } + + if (mtd->type != MTD_NANDFLASH) { + pr_info("this test requires NAND flash\n"); + goto out; + } + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + pgsize = mtd->writesize; + + pr_info("MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + pgsize, ebcnt, pgcnt, mtd->oobsize); + + err = -ENOMEM; + bufsize = pgsize * 2; + writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!writebuf) + goto out; + twopages = kmalloc(bufsize, GFP_KERNEL); + if (!twopages) + goto out; + boundary = kmalloc(bufsize, GFP_KERNEL); + if (!boundary) + goto out; + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + /* Erase all eraseblocks */ + pr_info("erasing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + pr_info("erased %u eraseblocks\n", i); + + /* Write all eraseblocks */ + prandom_seed_state(&rnd_state, 1); + pr_info("writing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock(i); + if (err) + goto out; + if (i % 256 == 0) + pr_info("written up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("written %u eraseblocks\n", i); + + /* Check all eraseblocks */ + prandom_seed_state(&rnd_state, 1); + pr_info("verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock(i); + if (err) + goto out; + if (i % 256 == 0) + pr_info("verified up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("verified %u eraseblocks\n", i); + + err = crosstest(); + if (err) + goto out; + + err = erasecrosstest(); + if (err) + goto out; + + err = erasetest(); + if (err) + goto out; + + pr_info("finished with %d errors\n", errcnt); +out: + + kfree(bbt); + kfree(boundary); + kfree(twopages); + kfree(writebuf); + put_mtd_device(mtd); + if (err) + pr_info("error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_pagetest_init); + +static void __exit mtd_pagetest_exit(void) +{ + return; +} +module_exit(mtd_pagetest_exit); + +MODULE_DESCRIPTION("NAND page test"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/readtest.c b/drivers/mtd/tests/readtest.c new file mode 100644 index 0000000..2cdd0c4 --- /dev/null +++ b/drivers/mtd/tests/readtest.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2006-2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Check MTD device read. + * + * Author: Adrian Hunter + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +static int dev = -EINVAL; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static struct mtd_info *mtd; +static unsigned char *iobuf; +static unsigned char *iobuf1; +static unsigned char *bbt; + +static int pgsize; +static int ebcnt; +static int pgcnt; + +static int read_eraseblock_by_page(int ebnum) +{ + size_t read; + int i, ret, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + void *oobbuf = iobuf1; + + for (i = 0; i < pgcnt; i++) { + memset(buf, 0 , pgsize); + ret = mtd_read(mtd, addr, pgsize, &read, buf); + if (ret == -EUCLEAN) + ret = 0; + if (ret || read != pgsize) { + pr_err("error: read failed at %#llx\n", + (long long)addr); + if (!err) + err = ret; + if (!err) + err = -EINVAL; + } + if (mtd->oobsize) { + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->oobsize; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.oobbuf = oobbuf; + ret = mtd_read_oob(mtd, addr, &ops); + if ((ret && !mtd_is_bitflip(ret)) || + ops.oobretlen != mtd->oobsize) { + pr_err("error: read oob failed at " + "%#llx\n", (long long)addr); + if (!err) + err = ret; + if (!err) + err = -EINVAL; + } + oobbuf += mtd->oobsize; + } + addr += pgsize; + buf += pgsize; + } + + return err; +} + +static void dump_eraseblock(int ebnum) +{ + int i, j, n; + char line[128]; + int pg, oob; + + pr_info("dumping eraseblock %d\n", ebnum); + n = mtd->erasesize; + for (i = 0; i < n;) { + char *p = line; + + p += sprintf(p, "%05x: ", i); + for (j = 0; j < 32 && i < n; j++, i++) + p += sprintf(p, "%02x", (unsigned int)iobuf[i]); + printk(KERN_CRIT "%s\n", line); + cond_resched(); + } + if (!mtd->oobsize) + return; + pr_info("dumping oob from eraseblock %d\n", ebnum); + n = mtd->oobsize; + for (pg = 0, i = 0; pg < pgcnt; pg++) + for (oob = 0; oob < n;) { + char *p = line; + + p += sprintf(p, "%05x: ", i); + for (j = 0; j < 32 && oob < n; j++, oob++, i++) + p += sprintf(p, "%02x", + (unsigned int)iobuf1[i]); + printk(KERN_CRIT "%s\n", line); + cond_resched(); + } +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd_block_isbad(mtd, addr); + if (ret) + pr_info("block %d is bad\n", ebnum); + return ret; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + return -ENOMEM; + + if (!mtd_can_have_bb(mtd)) + return 0; + + pr_info("scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_readtest_init(void) +{ + uint64_t tmp; + int err, i; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + + if (dev < 0) { + pr_info("Please specify a valid mtd-device via module parameter\n"); + return -EINVAL; + } + + pr_info("MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_err("error: Cannot get MTD device\n"); + return err; + } + + if (mtd->writesize == 1) { + pr_info("not NAND flash, assume page size is 512 " + "bytes.\n"); + pgsize = 512; + } else + pgsize = mtd->writesize; + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / pgsize; + + pr_info("MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + pgsize, ebcnt, pgcnt, mtd->oobsize); + + err = -ENOMEM; + iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!iobuf) + goto out; + iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!iobuf1) + goto out; + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + /* Read all eraseblocks 1 page at a time */ + pr_info("testing page read\n"); + for (i = 0; i < ebcnt; ++i) { + int ret; + + if (bbt[i]) + continue; + ret = read_eraseblock_by_page(i); + if (ret) { + dump_eraseblock(i); + if (!err) + err = ret; + } + cond_resched(); + } + + if (err) + pr_info("finished with errors\n"); + else + pr_info("finished\n"); + +out: + + kfree(iobuf); + kfree(iobuf1); + kfree(bbt); + put_mtd_device(mtd); + if (err) + pr_info("error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_readtest_init); + +static void __exit mtd_readtest_exit(void) +{ + return; +} +module_exit(mtd_readtest_exit); + +MODULE_DESCRIPTION("Read test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c new file mode 100644 index 0000000..20b63d1 --- /dev/null +++ b/drivers/mtd/tests/speedtest.c @@ -0,0 +1,556 @@ +/* + * Copyright (C) 2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test read and write speed of a MTD device. + * + * Author: Adrian Hunter + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +static int dev = -EINVAL; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static int count; +module_param(count, int, S_IRUGO); +MODULE_PARM_DESC(count, "Maximum number of eraseblocks to use " + "(0 means use all)"); + +static struct mtd_info *mtd; +static unsigned char *iobuf; +static unsigned char *bbt; + +static int pgsize; +static int ebcnt; +static int pgcnt; +static int goodebcnt; +static struct timeval start, finish; + + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd_erase(mtd, &ei); + if (err) { + pr_err("error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + pr_err("some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +static int multiblock_erase(int ebnum, int blocks) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize * blocks; + + err = mtd_erase(mtd, &ei); + if (err) { + pr_err("error %d while erasing EB %d, blocks %d\n", + err, ebnum, blocks); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + pr_err("some erase error occurred at EB %d," + "blocks %d\n", ebnum, blocks); + return -EIO; + } + + return 0; +} + +static int erase_whole_device(void) +{ + int err; + unsigned int i; + + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + return err; + cond_resched(); + } + return 0; +} + +static int write_eraseblock(int ebnum) +{ + size_t written; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf); + if (err || written != mtd->erasesize) { + pr_err("error: write failed at %#llx\n", addr); + if (!err) + err = -EINVAL; + } + + return err; +} + +static int write_eraseblock_by_page(int ebnum) +{ + size_t written; + int i, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + + for (i = 0; i < pgcnt; i++) { + err = mtd_write(mtd, addr, pgsize, &written, buf); + if (err || written != pgsize) { + pr_err("error: write failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + break; + } + addr += pgsize; + buf += pgsize; + } + + return err; +} + +static int write_eraseblock_by_2pages(int ebnum) +{ + size_t written, sz = pgsize * 2; + int i, n = pgcnt / 2, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + + for (i = 0; i < n; i++) { + err = mtd_write(mtd, addr, sz, &written, buf); + if (err || written != sz) { + pr_err("error: write failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + return err; + } + addr += sz; + buf += sz; + } + if (pgcnt % 2) { + err = mtd_write(mtd, addr, pgsize, &written, buf); + if (err || written != pgsize) { + pr_err("error: write failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + } + } + + return err; +} + +static int read_eraseblock(int ebnum) +{ + size_t read; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + err = mtd_read(mtd, addr, mtd->erasesize, &read, iobuf); + /* Ignore corrected ECC errors */ + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != mtd->erasesize) { + pr_err("error: read failed at %#llx\n", addr); + if (!err) + err = -EINVAL; + } + + return err; +} + +static int read_eraseblock_by_page(int ebnum) +{ + size_t read; + int i, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + + for (i = 0; i < pgcnt; i++) { + err = mtd_read(mtd, addr, pgsize, &read, buf); + /* Ignore corrected ECC errors */ + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + break; + } + addr += pgsize; + buf += pgsize; + } + + return err; +} + +static int read_eraseblock_by_2pages(int ebnum) +{ + size_t read, sz = pgsize * 2; + int i, n = pgcnt / 2, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + + for (i = 0; i < n; i++) { + err = mtd_read(mtd, addr, sz, &read, buf); + /* Ignore corrected ECC errors */ + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != sz) { + pr_err("error: read failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + return err; + } + addr += sz; + buf += sz; + } + if (pgcnt % 2) { + err = mtd_read(mtd, addr, pgsize, &read, buf); + /* Ignore corrected ECC errors */ + if (mtd_is_bitflip(err)) + err = 0; + if (err || read != pgsize) { + pr_err("error: read failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + } + } + + return err; +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd_block_isbad(mtd, addr); + if (ret) + pr_info("block %d is bad\n", ebnum); + return ret; +} + +static inline void start_timing(void) +{ + do_gettimeofday(&start); +} + +static inline void stop_timing(void) +{ + do_gettimeofday(&finish); +} + +static long calc_speed(void) +{ + uint64_t k; + long ms; + + ms = (finish.tv_sec - start.tv_sec) * 1000 + + (finish.tv_usec - start.tv_usec) / 1000; + if (ms == 0) + return 0; + k = goodebcnt * (mtd->erasesize / 1024) * 1000; + do_div(k, ms); + return k; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + return -ENOMEM; + + if (!mtd_can_have_bb(mtd)) + goto out; + + pr_info("scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); +out: + goodebcnt = ebcnt - bad; + return 0; +} + +static int __init mtd_speedtest_init(void) +{ + int err, i, blocks, j, k; + long speed; + uint64_t tmp; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + + if (dev < 0) { + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); + return -EINVAL; + } + + if (count) + pr_info("MTD device: %d count: %d\n", dev, count); + else + pr_info("MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_err("error: cannot get MTD device\n"); + return err; + } + + if (mtd->writesize == 1) { + pr_info("not NAND flash, assume page size is 512 " + "bytes.\n"); + pgsize = 512; + } else + pgsize = mtd->writesize; + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / pgsize; + + pr_info("MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + pgsize, ebcnt, pgcnt, mtd->oobsize); + + if (count > 0 && count < ebcnt) + ebcnt = count; + + err = -ENOMEM; + iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!iobuf) + goto out; + + prandom_bytes(iobuf, mtd->erasesize); + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks, 1 eraseblock at a time */ + pr_info("testing eraseblock write speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + pr_info("eraseblock write speed is %ld KiB/s\n", speed); + + /* Read all eraseblocks, 1 eraseblock at a time */ + pr_info("testing eraseblock read speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = read_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + pr_info("eraseblock read speed is %ld KiB/s\n", speed); + + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks, 1 page at a time */ + pr_info("testing page write speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock_by_page(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + pr_info("page write speed is %ld KiB/s\n", speed); + + /* Read all eraseblocks, 1 page at a time */ + pr_info("testing page read speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = read_eraseblock_by_page(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + pr_info("page read speed is %ld KiB/s\n", speed); + + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks, 2 pages at a time */ + pr_info("testing 2 page write speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock_by_2pages(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + pr_info("2 page write speed is %ld KiB/s\n", speed); + + /* Read all eraseblocks, 2 pages at a time */ + pr_info("testing 2 page read speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = read_eraseblock_by_2pages(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + pr_info("2 page read speed is %ld KiB/s\n", speed); + + /* Erase all eraseblocks */ + pr_info("Testing erase speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + pr_info("erase speed is %ld KiB/s\n", speed); + + /* Multi-block erase all eraseblocks */ + for (k = 1; k < 7; k++) { + blocks = 1 << k; + pr_info("Testing %dx multi-block erase speed\n", + blocks); + start_timing(); + for (i = 0; i < ebcnt; ) { + for (j = 0; j < blocks && (i + j) < ebcnt; j++) + if (bbt[i + j]) + break; + if (j < 1) { + i++; + continue; + } + err = multiblock_erase(i, j); + if (err) + goto out; + cond_resched(); + i += j; + } + stop_timing(); + speed = calc_speed(); + pr_info("%dx multi-block erase speed is %ld KiB/s\n", + blocks, speed); + } + pr_info("finished\n"); +out: + kfree(iobuf); + kfree(bbt); + put_mtd_device(mtd); + if (err) + pr_info("error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_speedtest_init); + +static void __exit mtd_speedtest_exit(void) +{ + return; +} +module_exit(mtd_speedtest_exit); + +MODULE_DESCRIPTION("Speed test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/stresstest.c b/drivers/mtd/tests/stresstest.c new file mode 100644 index 0000000..3a95e61 --- /dev/null +++ b/drivers/mtd/tests/stresstest.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2006-2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test random reads, writes and erases on MTD device. + * + * Author: Adrian Hunter + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int dev = -EINVAL; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static int count = 10000; +module_param(count, int, S_IRUGO); +MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)"); + +static struct mtd_info *mtd; +static unsigned char *writebuf; +static unsigned char *readbuf; +static unsigned char *bbt; +static int *offsets; + +static int pgsize; +static int bufsize; +static int ebcnt; +static int pgcnt; + +static int rand_eb(void) +{ + unsigned int eb; + +again: + eb = prandom_u32(); + /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ + eb %= (ebcnt - 1); + if (bbt[eb]) + goto again; + return eb; +} + +static int rand_offs(void) +{ + unsigned int offs; + + offs = prandom_u32(); + offs %= bufsize; + return offs; +} + +static int rand_len(int offs) +{ + unsigned int len; + + len = prandom_u32(); + len %= (bufsize - offs); + return len; +} + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd_erase(mtd, &ei); + if (unlikely(err)) { + pr_err("error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (unlikely(ei.state == MTD_ERASE_FAILED)) { + pr_err("some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd_block_isbad(mtd, addr); + if (ret) + pr_info("block %d is bad\n", ebnum); + return ret; +} + +static int do_read(void) +{ + size_t read; + int eb = rand_eb(); + int offs = rand_offs(); + int len = rand_len(offs), err; + loff_t addr; + + if (bbt[eb + 1]) { + if (offs >= mtd->erasesize) + offs -= mtd->erasesize; + if (offs + len > mtd->erasesize) + len = mtd->erasesize - offs; + } + addr = eb * mtd->erasesize + offs; + err = mtd_read(mtd, addr, len, &read, readbuf); + if (mtd_is_bitflip(err)) + err = 0; + if (unlikely(err || read != len)) { + pr_err("error: read failed at 0x%llx\n", + (long long)addr); + if (!err) + err = -EINVAL; + return err; + } + return 0; +} + +static int do_write(void) +{ + int eb = rand_eb(), offs, err, len; + size_t written; + loff_t addr; + + offs = offsets[eb]; + if (offs >= mtd->erasesize) { + err = erase_eraseblock(eb); + if (err) + return err; + offs = offsets[eb] = 0; + } + len = rand_len(offs); + len = ((len + pgsize - 1) / pgsize) * pgsize; + if (offs + len > mtd->erasesize) { + if (bbt[eb + 1]) + len = mtd->erasesize - offs; + else { + err = erase_eraseblock(eb + 1); + if (err) + return err; + offsets[eb + 1] = 0; + } + } + addr = eb * mtd->erasesize + offs; + err = mtd_write(mtd, addr, len, &written, writebuf); + if (unlikely(err || written != len)) { + pr_err("error: write failed at 0x%llx\n", + (long long)addr); + if (!err) + err = -EINVAL; + return err; + } + offs += len; + while (offs > mtd->erasesize) { + offsets[eb++] = mtd->erasesize; + offs -= mtd->erasesize; + } + offsets[eb] = offs; + return 0; +} + +static int do_operation(void) +{ + if (prandom_u32() & 1) + return do_read(); + else + return do_write(); +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + return -ENOMEM; + + if (!mtd_can_have_bb(mtd)) + return 0; + + pr_info("scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_stresstest_init(void) +{ + int err; + int i, op; + uint64_t tmp; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + + if (dev < 0) { + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); + return -EINVAL; + } + + pr_info("MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_err("error: cannot get MTD device\n"); + return err; + } + + if (mtd->writesize == 1) { + pr_info("not NAND flash, assume page size is 512 " + "bytes.\n"); + pgsize = 512; + } else + pgsize = mtd->writesize; + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / pgsize; + + pr_info("MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + pgsize, ebcnt, pgcnt, mtd->oobsize); + + if (ebcnt < 2) { + pr_err("error: need at least 2 eraseblocks\n"); + err = -ENOSPC; + goto out_put_mtd; + } + + /* Read or write up 2 eraseblocks at a time */ + bufsize = mtd->erasesize * 2; + + err = -ENOMEM; + readbuf = vmalloc(bufsize); + writebuf = vmalloc(bufsize); + offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL); + if (!readbuf || !writebuf || !offsets) + goto out; + for (i = 0; i < ebcnt; i++) + offsets[i] = mtd->erasesize; + prandom_bytes(writebuf, bufsize); + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + /* Do operations */ + pr_info("doing operations\n"); + for (op = 0; op < count; op++) { + if ((op & 1023) == 0) + pr_info("%d operations done\n", op); + err = do_operation(); + if (err) + goto out; + cond_resched(); + } + pr_info("finished, %d operations done\n", op); + +out: + kfree(offsets); + kfree(bbt); + vfree(writebuf); + vfree(readbuf); +out_put_mtd: + put_mtd_device(mtd); + if (err) + pr_info("error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_stresstest_init); + +static void __exit mtd_stresstest_exit(void) +{ + return; +} +module_exit(mtd_stresstest_exit); + +MODULE_DESCRIPTION("Stress test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/subpagetest.c b/drivers/mtd/tests/subpagetest.c new file mode 100644 index 0000000..e41a04f --- /dev/null +++ b/drivers/mtd/tests/subpagetest.c @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2006-2007 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test sub-page read and write on MTD device. + * Author: Adrian Hunter + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +static int dev = -EINVAL; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static struct mtd_info *mtd; +static unsigned char *writebuf; +static unsigned char *readbuf; +static unsigned char *bbt; + +static int subpgsize; +static int bufsize; +static int ebcnt; +static int pgcnt; +static int errcnt; +static struct rnd_state rnd_state; + +static inline void clear_data(unsigned char *buf, size_t len) +{ + memset(buf, 0, len); +} + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd_erase(mtd, &ei); + if (err) { + pr_err("error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + pr_err("some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +static int erase_whole_device(void) +{ + int err; + unsigned int i; + + pr_info("erasing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + return err; + cond_resched(); + } + pr_info("erased %u eraseblocks\n", i); + return 0; +} + +static int write_eraseblock(int ebnum) +{ + size_t written; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + prandom_bytes_state(&rnd_state, writebuf, subpgsize); + err = mtd_write(mtd, addr, subpgsize, &written, writebuf); + if (unlikely(err || written != subpgsize)) { + pr_err("error: write failed at %#llx\n", + (long long)addr); + if (written != subpgsize) { + pr_err(" write size: %#x\n", subpgsize); + pr_err(" written: %#zx\n", written); + } + return err ? err : -1; + } + + addr += subpgsize; + + prandom_bytes_state(&rnd_state, writebuf, subpgsize); + err = mtd_write(mtd, addr, subpgsize, &written, writebuf); + if (unlikely(err || written != subpgsize)) { + pr_err("error: write failed at %#llx\n", + (long long)addr); + if (written != subpgsize) { + pr_err(" write size: %#x\n", subpgsize); + pr_err(" written: %#zx\n", written); + } + return err ? err : -1; + } + + return err; +} + +static int write_eraseblock2(int ebnum) +{ + size_t written; + int err = 0, k; + loff_t addr = ebnum * mtd->erasesize; + + for (k = 1; k < 33; ++k) { + if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) + break; + prandom_bytes_state(&rnd_state, writebuf, subpgsize * k); + err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf); + if (unlikely(err || written != subpgsize * k)) { + pr_err("error: write failed at %#llx\n", + (long long)addr); + if (written != subpgsize) { + pr_err(" write size: %#x\n", + subpgsize * k); + pr_err(" written: %#08zx\n", + written); + } + return err ? err : -1; + } + addr += subpgsize * k; + } + + return err; +} + +static void print_subpage(unsigned char *p) +{ + int i, j; + + for (i = 0; i < subpgsize; ) { + for (j = 0; i < subpgsize && j < 32; ++i, ++j) + printk("%02x", *p++); + printk("\n"); + } +} + +static int verify_eraseblock(int ebnum) +{ + size_t read; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + prandom_bytes_state(&rnd_state, writebuf, subpgsize); + clear_data(readbuf, subpgsize); + err = mtd_read(mtd, addr, subpgsize, &read, readbuf); + if (unlikely(err || read != subpgsize)) { + if (mtd_is_bitflip(err) && read == subpgsize) { + pr_info("ECC correction at %#llx\n", + (long long)addr); + err = 0; + } else { + pr_err("error: read failed at %#llx\n", + (long long)addr); + return err ? err : -1; + } + } + if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { + pr_err("error: verify failed at %#llx\n", + (long long)addr); + pr_info("------------- written----------------\n"); + print_subpage(writebuf); + pr_info("------------- read ------------------\n"); + print_subpage(readbuf); + pr_info("-------------------------------------\n"); + errcnt += 1; + } + + addr += subpgsize; + + prandom_bytes_state(&rnd_state, writebuf, subpgsize); + clear_data(readbuf, subpgsize); + err = mtd_read(mtd, addr, subpgsize, &read, readbuf); + if (unlikely(err || read != subpgsize)) { + if (mtd_is_bitflip(err) && read == subpgsize) { + pr_info("ECC correction at %#llx\n", + (long long)addr); + err = 0; + } else { + pr_err("error: read failed at %#llx\n", + (long long)addr); + return err ? err : -1; + } + } + if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { + pr_info("error: verify failed at %#llx\n", + (long long)addr); + pr_info("------------- written----------------\n"); + print_subpage(writebuf); + pr_info("------------- read ------------------\n"); + print_subpage(readbuf); + pr_info("-------------------------------------\n"); + errcnt += 1; + } + + return err; +} + +static int verify_eraseblock2(int ebnum) +{ + size_t read; + int err = 0, k; + loff_t addr = ebnum * mtd->erasesize; + + for (k = 1; k < 33; ++k) { + if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) + break; + prandom_bytes_state(&rnd_state, writebuf, subpgsize * k); + clear_data(readbuf, subpgsize * k); + err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf); + if (unlikely(err || read != subpgsize * k)) { + if (mtd_is_bitflip(err) && read == subpgsize * k) { + pr_info("ECC correction at %#llx\n", + (long long)addr); + err = 0; + } else { + pr_err("error: read failed at " + "%#llx\n", (long long)addr); + return err ? err : -1; + } + } + if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) { + pr_err("error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + } + addr += subpgsize * k; + } + + return err; +} + +static int verify_eraseblock_ff(int ebnum) +{ + uint32_t j; + size_t read; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + memset(writebuf, 0xff, subpgsize); + for (j = 0; j < mtd->erasesize / subpgsize; ++j) { + clear_data(readbuf, subpgsize); + err = mtd_read(mtd, addr, subpgsize, &read, readbuf); + if (unlikely(err || read != subpgsize)) { + if (mtd_is_bitflip(err) && read == subpgsize) { + pr_info("ECC correction at %#llx\n", + (long long)addr); + err = 0; + } else { + pr_err("error: read failed at " + "%#llx\n", (long long)addr); + return err ? err : -1; + } + } + if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { + pr_err("error: verify 0xff failed at " + "%#llx\n", (long long)addr); + errcnt += 1; + } + addr += subpgsize; + } + + return err; +} + +static int verify_all_eraseblocks_ff(void) +{ + int err; + unsigned int i; + + pr_info("verifying all eraseblocks for 0xff\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock_ff(i); + if (err) + return err; + if (i % 256 == 0) + pr_info("verified up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("verified %u eraseblocks\n", i); + return 0; +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd_block_isbad(mtd, addr); + if (ret) + pr_info("block %d is bad\n", ebnum); + return ret; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + return -ENOMEM; + + pr_info("scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_subpagetest_init(void) +{ + int err = 0; + uint32_t i; + uint64_t tmp; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + + if (dev < 0) { + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); + return -EINVAL; + } + + pr_info("MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_err("error: cannot get MTD device\n"); + return err; + } + + if (mtd->type != MTD_NANDFLASH) { + pr_info("this test requires NAND flash\n"); + goto out; + } + + subpgsize = mtd->writesize >> mtd->subpage_sft; + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + + pr_info("MTD device size %llu, eraseblock size %u, " + "page size %u, subpage size %u, count of eraseblocks %u, " + "pages per eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize); + + err = -ENOMEM; + bufsize = subpgsize * 32; + writebuf = kmalloc(bufsize, GFP_KERNEL); + if (!writebuf) + goto out; + readbuf = kmalloc(bufsize, GFP_KERNEL); + if (!readbuf) + goto out; + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + err = erase_whole_device(); + if (err) + goto out; + + pr_info("writing whole device\n"); + prandom_seed_state(&rnd_state, 1); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock(i); + if (unlikely(err)) + goto out; + if (i % 256 == 0) + pr_info("written up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("written %u eraseblocks\n", i); + + prandom_seed_state(&rnd_state, 1); + pr_info("verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock(i); + if (unlikely(err)) + goto out; + if (i % 256 == 0) + pr_info("verified up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("verified %u eraseblocks\n", i); + + err = erase_whole_device(); + if (err) + goto out; + + err = verify_all_eraseblocks_ff(); + if (err) + goto out; + + /* Write all eraseblocks */ + prandom_seed_state(&rnd_state, 3); + pr_info("writing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock2(i); + if (unlikely(err)) + goto out; + if (i % 256 == 0) + pr_info("written up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("written %u eraseblocks\n", i); + + /* Check all eraseblocks */ + prandom_seed_state(&rnd_state, 3); + pr_info("verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock2(i); + if (unlikely(err)) + goto out; + if (i % 256 == 0) + pr_info("verified up to eraseblock %u\n", i); + cond_resched(); + } + pr_info("verified %u eraseblocks\n", i); + + err = erase_whole_device(); + if (err) + goto out; + + err = verify_all_eraseblocks_ff(); + if (err) + goto out; + + pr_info("finished with %d errors\n", errcnt); + +out: + kfree(bbt); + kfree(readbuf); + kfree(writebuf); + put_mtd_device(mtd); + if (err) + pr_info("error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_subpagetest_init); + +static void __exit mtd_subpagetest_exit(void) +{ + return; +} +module_exit(mtd_subpagetest_exit); + +MODULE_DESCRIPTION("Subpage test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/torturetest.c b/drivers/mtd/tests/torturetest.c new file mode 100644 index 0000000..3a9f6a6 --- /dev/null +++ b/drivers/mtd/tests/torturetest.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2006-2008 Artem Bityutskiy + * Copyright (C) 2006-2008 Jarkko Lavinen + * Copyright (C) 2006-2008 Adrian Hunter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter + * + * WARNING: this test program may kill your flash and your device. Do not + * use it unless you know what you do. Authors are not responsible for any + * damage caused by this program. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#define RETRIES 3 + +static int eb = 8; +module_param(eb, int, S_IRUGO); +MODULE_PARM_DESC(eb, "eraseblock number within the selected MTD device"); + +static int ebcnt = 32; +module_param(ebcnt, int, S_IRUGO); +MODULE_PARM_DESC(ebcnt, "number of consecutive eraseblocks to torture"); + +static int pgcnt; +module_param(pgcnt, int, S_IRUGO); +MODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)"); + +static int dev = -EINVAL; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static int gran = 512; +module_param(gran, int, S_IRUGO); +MODULE_PARM_DESC(gran, "how often the status information should be printed"); + +static int check = 1; +module_param(check, int, S_IRUGO); +MODULE_PARM_DESC(check, "if the written data should be checked"); + +static unsigned int cycles_count; +module_param(cycles_count, uint, S_IRUGO); +MODULE_PARM_DESC(cycles_count, "how many erase cycles to do " + "(infinite by default)"); + +static struct mtd_info *mtd; + +/* This buffer contains 0x555555...0xAAAAAA... pattern */ +static unsigned char *patt_5A5; +/* This buffer contains 0xAAAAAA...0x555555... pattern */ +static unsigned char *patt_A5A; +/* This buffer contains all 0xFF bytes */ +static unsigned char *patt_FF; +/* This a temporary buffer is use when checking data */ +static unsigned char *check_buf; +/* How many erase cycles were done */ +static unsigned int erase_cycles; + +static int pgsize; +static struct timeval start, finish; + +static void report_corrupt(unsigned char *read, unsigned char *written); + +static inline void start_timing(void) +{ + do_gettimeofday(&start); +} + +static inline void stop_timing(void) +{ + do_gettimeofday(&finish); +} + +/* + * Erase eraseblock number @ebnum. + */ +static inline int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd_erase(mtd, &ei); + if (err) { + pr_err("error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + pr_err("some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +/* + * Check that the contents of eraseblock number @enbum is equivalent to the + * @buf buffer. + */ +static inline int check_eraseblock(int ebnum, unsigned char *buf) +{ + int err, retries = 0; + size_t read; + loff_t addr = ebnum * mtd->erasesize; + size_t len = mtd->erasesize; + + if (pgcnt) { + addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize; + len = pgcnt * pgsize; + } + +retry: + err = mtd_read(mtd, addr, len, &read, check_buf); + if (mtd_is_bitflip(err)) + pr_err("single bit flip occurred at EB %d " + "MTD reported that it was fixed.\n", ebnum); + else if (err) { + pr_err("error %d while reading EB %d, " + "read %zd\n", err, ebnum, read); + return err; + } + + if (read != len) { + pr_err("failed to read %zd bytes from EB %d, " + "read only %zd, but no error reported\n", + len, ebnum, read); + return -EIO; + } + + if (memcmp(buf, check_buf, len)) { + pr_err("read wrong data from EB %d\n", ebnum); + report_corrupt(check_buf, buf); + + if (retries++ < RETRIES) { + /* Try read again */ + yield(); + pr_info("re-try reading data from EB %d\n", + ebnum); + goto retry; + } else { + pr_info("retried %d times, still errors, " + "give-up\n", RETRIES); + return -EINVAL; + } + } + + if (retries != 0) + pr_info("only attempt number %d was OK (!!!)\n", + retries); + + return 0; +} + +static inline int write_pattern(int ebnum, void *buf) +{ + int err; + size_t written; + loff_t addr = ebnum * mtd->erasesize; + size_t len = mtd->erasesize; + + if (pgcnt) { + addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize; + len = pgcnt * pgsize; + } + err = mtd_write(mtd, addr, len, &written, buf); + if (err) { + pr_err("error %d while writing EB %d, written %zd" + " bytes\n", err, ebnum, written); + return err; + } + if (written != len) { + pr_info("written only %zd bytes of %zd, but no error" + " reported\n", written, len); + return -EIO; + } + + return 0; +} + +static int __init tort_init(void) +{ + int err = 0, i, infinite = !cycles_count; + int *bad_ebs; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + pr_info("Warning: this program is trying to wear out your " + "flash, stop it if this is not wanted.\n"); + + if (dev < 0) { + pr_info("Please specify a valid mtd-device via module parameter\n"); + pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); + return -EINVAL; + } + + pr_info("MTD device: %d\n", dev); + pr_info("torture %d eraseblocks (%d-%d) of mtd%d\n", + ebcnt, eb, eb + ebcnt - 1, dev); + if (pgcnt) + pr_info("torturing just %d pages per eraseblock\n", + pgcnt); + pr_info("write verify %s\n", check ? "enabled" : "disabled"); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_err("error: cannot get MTD device\n"); + return err; + } + + if (mtd->writesize == 1) { + pr_info("not NAND flash, assume page size is 512 " + "bytes.\n"); + pgsize = 512; + } else + pgsize = mtd->writesize; + + if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) { + pr_err("error: invalid pgcnt value %d\n", pgcnt); + goto out_mtd; + } + + err = -ENOMEM; + patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!patt_5A5) + goto out_mtd; + + patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!patt_A5A) + goto out_patt_5A5; + + patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!patt_FF) + goto out_patt_A5A; + + check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!check_buf) + goto out_patt_FF; + + bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL); + if (!bad_ebs) + goto out_check_buf; + + err = 0; + + /* Initialize patterns */ + memset(patt_FF, 0xFF, mtd->erasesize); + for (i = 0; i < mtd->erasesize / pgsize; i++) { + if (!(i & 1)) { + memset(patt_5A5 + i * pgsize, 0x55, pgsize); + memset(patt_A5A + i * pgsize, 0xAA, pgsize); + } else { + memset(patt_5A5 + i * pgsize, 0xAA, pgsize); + memset(patt_A5A + i * pgsize, 0x55, pgsize); + } + } + + /* + * Check if there is a bad eraseblock among those we are going to test. + */ + if (mtd_can_have_bb(mtd)) { + for (i = eb; i < eb + ebcnt; i++) { + err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); + + if (err < 0) { + pr_info("block_isbad() returned %d " + "for EB %d\n", err, i); + goto out; + } + + if (err) { + pr_err("EB %d is bad. Skip it.\n", i); + bad_ebs[i - eb] = 1; + } + } + } + + start_timing(); + while (1) { + int i; + void *patt; + + /* Erase all eraseblocks */ + for (i = eb; i < eb + ebcnt; i++) { + if (bad_ebs[i - eb]) + continue; + err = erase_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + + /* Check if the eraseblocks contain only 0xFF bytes */ + if (check) { + for (i = eb; i < eb + ebcnt; i++) { + if (bad_ebs[i - eb]) + continue; + err = check_eraseblock(i, patt_FF); + if (err) { + pr_info("verify failed" + " for 0xFF... pattern\n"); + goto out; + } + cond_resched(); + } + } + + /* Write the pattern */ + for (i = eb; i < eb + ebcnt; i++) { + if (bad_ebs[i - eb]) + continue; + if ((eb + erase_cycles) & 1) + patt = patt_5A5; + else + patt = patt_A5A; + err = write_pattern(i, patt); + if (err) + goto out; + cond_resched(); + } + + /* Verify what we wrote */ + if (check) { + for (i = eb; i < eb + ebcnt; i++) { + if (bad_ebs[i - eb]) + continue; + if ((eb + erase_cycles) & 1) + patt = patt_5A5; + else + patt = patt_A5A; + err = check_eraseblock(i, patt); + if (err) { + pr_info("verify failed for %s" + " pattern\n", + ((eb + erase_cycles) & 1) ? + "0x55AA55..." : "0xAA55AA..."); + goto out; + } + cond_resched(); + } + } + + erase_cycles += 1; + + if (erase_cycles % gran == 0) { + long ms; + + stop_timing(); + ms = (finish.tv_sec - start.tv_sec) * 1000 + + (finish.tv_usec - start.tv_usec) / 1000; + pr_info("%08u erase cycles done, took %lu " + "milliseconds (%lu seconds)\n", + erase_cycles, ms, ms / 1000); + start_timing(); + } + + if (!infinite && --cycles_count == 0) + break; + } +out: + + pr_info("finished after %u erase cycles\n", + erase_cycles); + kfree(bad_ebs); +out_check_buf: + kfree(check_buf); +out_patt_FF: + kfree(patt_FF); +out_patt_A5A: + kfree(patt_A5A); +out_patt_5A5: + kfree(patt_5A5); +out_mtd: + put_mtd_device(mtd); + if (err) + pr_info("error %d occurred during torturing\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(tort_init); + +static void __exit tort_exit(void) +{ + return; +} +module_exit(tort_exit); + +static int countdiffs(unsigned char *buf, unsigned char *check_buf, + unsigned offset, unsigned len, unsigned *bytesp, + unsigned *bitsp); +static void print_bufs(unsigned char *read, unsigned char *written, int start, + int len); + +/* + * Report the detailed information about how the read EB differs from what was + * written. + */ +static void report_corrupt(unsigned char *read, unsigned char *written) +{ + int i; + int bytes, bits, pages, first; + int offset, len; + size_t check_len = mtd->erasesize; + + if (pgcnt) + check_len = pgcnt * pgsize; + + bytes = bits = pages = 0; + for (i = 0; i < check_len; i += pgsize) + if (countdiffs(written, read, i, pgsize, &bytes, + &bits) >= 0) + pages++; + + pr_info("verify fails on %d pages, %d bytes/%d bits\n", + pages, bytes, bits); + pr_info("The following is a list of all differences between" + " what was read from flash and what was expected\n"); + + for (i = 0; i < check_len; i += pgsize) { + cond_resched(); + bytes = bits = 0; + first = countdiffs(written, read, i, pgsize, &bytes, + &bits); + if (first < 0) + continue; + + printk("-------------------------------------------------------" + "----------------------------------\n"); + + pr_info("Page %zd has %d bytes/%d bits failing verify," + " starting at offset 0x%x\n", + (mtd->erasesize - check_len + i) / pgsize, + bytes, bits, first); + + offset = first & ~0x7; + len = ((first + bytes) | 0x7) + 1 - offset; + + print_bufs(read, written, offset, len); + } +} + +static void print_bufs(unsigned char *read, unsigned char *written, int start, + int len) +{ + int i = 0, j1, j2; + char *diff; + + printk("Offset Read Written\n"); + while (i < len) { + printk("0x%08x: ", start + i); + diff = " "; + for (j1 = 0; j1 < 8 && i + j1 < len; j1++) { + printk(" %02x", read[start + i + j1]); + if (read[start + i + j1] != written[start + i + j1]) + diff = "***"; + } + + while (j1 < 8) { + printk(" "); + j1 += 1; + } + + printk(" %s ", diff); + + for (j2 = 0; j2 < 8 && i + j2 < len; j2++) + printk(" %02x", written[start + i + j2]); + printk("\n"); + i += 8; + } +} + +/* + * Count the number of differing bytes and bits and return the first differing + * offset. + */ +static int countdiffs(unsigned char *buf, unsigned char *check_buf, + unsigned offset, unsigned len, unsigned *bytesp, + unsigned *bitsp) +{ + unsigned i, bit; + int first = -1; + + for (i = offset; i < offset + len; i++) + if (buf[i] != check_buf[i]) { + first = i; + break; + } + + while (i < offset + len) { + if (buf[i] != check_buf[i]) { + (*bytesp)++; + bit = 1; + while (bit < 256) { + if ((buf[i] & bit) != (check_buf[i] & bit)) + (*bitsp)++; + bit <<= 1; + } + } + i++; + } + + return first; +} + +MODULE_DESCRIPTION("Eraseblock torturing module"); +MODULE_AUTHOR("Artem Bityutskiy, Jarkko Lavinen, Adrian Hunter"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 4bf527aa5312655d478a49f545a355ee9e76175d Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:09 +0900 Subject: mtd: mtd_oobtest: use mtd_test helpers Use mtdtest_scan_for_bad_eraseblocks(), mtdtest_erase_good_eraseblocks(), and mtdtest_erase_eraseblock() in mtd_test helpers. Signed-off-by: Akinobu Mita Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c index ab81e9a..ff35c46 100644 --- a/drivers/mtd/tests/oobtest.c +++ b/drivers/mtd/tests/oobtest.c @@ -31,6 +31,8 @@ #include #include +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -49,49 +51,6 @@ static int use_len_max; static int vary_offset; static struct rnd_state rnd_state; -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", ebnum); - return -EIO; - } - - return 0; -} - -static int erase_whole_device(void) -{ - int err; - unsigned int i; - - pr_info("erasing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - return err; - cond_resched(); - } - pr_info("erased %u eraseblocks\n", i); - return 0; -} - static void do_vary_offset(void) { use_len -= 1; @@ -304,36 +263,6 @@ static int verify_all_eraseblocks(void) return 0; } -static int is_block_bad(int ebnum) -{ - int ret; - loff_t addr = ebnum * mtd->erasesize; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kmalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_oobtest_init(void) { int err = 0; @@ -383,8 +312,11 @@ static int __init mtd_oobtest_init(void) writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!writebuf) goto out; + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; - err = scan_for_bad_eraseblocks(); + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -396,7 +328,7 @@ static int __init mtd_oobtest_init(void) /* First test: write all OOB, read it back and verify */ pr_info("test 1 of 5\n"); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -416,7 +348,7 @@ static int __init mtd_oobtest_init(void) */ pr_info("test 2 of 5\n"); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -446,7 +378,7 @@ static int __init mtd_oobtest_init(void) */ pr_info("test 3 of 5\n"); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -479,7 +411,7 @@ static int __init mtd_oobtest_init(void) /* Fourth test: try to write off end of device */ pr_info("test 4 of 5\n"); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -571,7 +503,7 @@ static int __init mtd_oobtest_init(void) errcnt += 1; } - err = erase_eraseblock(ebcnt - 1); + err = mtdtest_erase_eraseblock(mtd, ebcnt - 1); if (err) goto out; @@ -620,7 +552,7 @@ static int __init mtd_oobtest_init(void) pr_info("test 5 of 5\n"); /* Erase all eraseblocks */ - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; -- cgit v0.10.2 From 66b28183eeba83998a52bd6e598aa2abbe877ef3 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:10 +0900 Subject: mtd: mtd_pagetest: use mtd_test helpers Use mtdtest_write(), mtdtest_read(), mtdtest_erase_eraseblock(), mtdtest_scan_for_bad_eraseblocks(), and mtdtest_erase_good_eraseblocks() in mtd_test helpers. [dwmw2: merge later 'remove always true condition' fix] Signed-off-by: Akinobu Mita Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c index acd991f..8318976 100644 --- a/drivers/mtd/tests/pagetest.c +++ b/drivers/mtd/tests/pagetest.c @@ -31,6 +31,8 @@ #include #include +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -48,42 +50,15 @@ static int pgcnt; static int errcnt; static struct rnd_state rnd_state; -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - static int write_eraseblock(int ebnum) { - int err = 0; - size_t written; + int err; loff_t addr = ebnum * mtd->erasesize; prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); cond_resched(); - err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf); - if (err || written != mtd->erasesize) + err = mtdtest_write(mtd, addr, mtd->erasesize, writebuf); + if (err) pr_err("error: write failed at %#llx\n", (long long)addr); @@ -93,7 +68,6 @@ static int write_eraseblock(int ebnum) static int verify_eraseblock(int ebnum) { uint32_t j; - size_t read; int err = 0, i; loff_t addr0, addrn; loff_t addr = ebnum * mtd->erasesize; @@ -109,27 +83,21 @@ static int verify_eraseblock(int ebnum) prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { /* Do a read to set the internal dataRAMs to different data */ - err = mtd_read(mtd, addr0, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { + err = mtdtest_read(mtd, addr0, bufsize, twopages); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr0); return err; } - err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { + err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages); + if (err) { pr_err("error: read failed at %#llx\n", (long long)(addrn - bufsize)); return err; } memset(twopages, 0, bufsize); - err = mtd_read(mtd, addr, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { + err = mtdtest_read(mtd, addr, bufsize, twopages); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr); break; @@ -145,27 +113,21 @@ static int verify_eraseblock(int ebnum) struct rnd_state old_state = rnd_state; /* Do a read to set the internal dataRAMs to different data */ - err = mtd_read(mtd, addr0, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { + err = mtdtest_read(mtd, addr0, bufsize, twopages); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr0); return err; } - err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { + err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages); + if (err) { pr_err("error: read failed at %#llx\n", (long long)(addrn - bufsize)); return err; } memset(twopages, 0, bufsize); - err = mtd_read(mtd, addr, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { + err = mtdtest_read(mtd, addr, bufsize, twopages); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr); return err; @@ -184,7 +146,6 @@ static int verify_eraseblock(int ebnum) static int crosstest(void) { - size_t read; int err = 0, i; loff_t addr, addr0, addrn; unsigned char *pp1, *pp2, *pp3, *pp4; @@ -208,10 +169,8 @@ static int crosstest(void) /* Read 2nd-to-last page to pp1 */ addr = addrn - pgsize - pgsize; - err = mtd_read(mtd, addr, pgsize, &read, pp1); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr, pgsize, pp1); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); @@ -220,10 +179,8 @@ static int crosstest(void) /* Read 3rd-to-last page to pp1 */ addr = addrn - pgsize - pgsize - pgsize; - err = mtd_read(mtd, addr, pgsize, &read, pp1); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr, pgsize, pp1); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); @@ -233,10 +190,8 @@ static int crosstest(void) /* Read first page to pp2 */ addr = addr0; pr_info("reading page at %#llx\n", (long long)addr); - err = mtd_read(mtd, addr, pgsize, &read, pp2); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr, pgsize, pp2); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); @@ -246,10 +201,8 @@ static int crosstest(void) /* Read last page to pp3 */ addr = addrn - pgsize; pr_info("reading page at %#llx\n", (long long)addr); - err = mtd_read(mtd, addr, pgsize, &read, pp3); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr, pgsize, pp3); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); @@ -259,10 +212,8 @@ static int crosstest(void) /* Read first page again to pp4 */ addr = addr0; pr_info("reading page at %#llx\n", (long long)addr); - err = mtd_read(mtd, addr, pgsize, &read, pp4); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr, pgsize, pp4); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr); kfree(pp1); @@ -283,7 +234,6 @@ static int crosstest(void) static int erasecrosstest(void) { - size_t read, written; int err = 0, i, ebnum, ebnum2; loff_t addr0; char *readbuf = twopages; @@ -302,29 +252,27 @@ static int erasecrosstest(void) ebnum2 -= 1; pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); + err = mtdtest_erase_eraseblock(mtd, ebnum); if (err) return err; pr_info("writing 1st page of block %d\n", ebnum); prandom_bytes_state(&rnd_state, writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); - err = mtd_write(mtd, addr0, pgsize, &written, writebuf); - if (err || written != pgsize) { + err = mtdtest_write(mtd, addr0, pgsize, writebuf); + if (err) { pr_info("error: write failed at %#llx\n", (long long)addr0); - return err ? err : -1; + return err; } pr_info("reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); - err = mtd_read(mtd, addr0, pgsize, &read, readbuf); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr0, pgsize, readbuf); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr0); - return err ? err : -1; + return err; } pr_info("verifying 1st page of block %d\n", ebnum); @@ -335,34 +283,32 @@ static int erasecrosstest(void) } pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); + err = mtdtest_erase_eraseblock(mtd, ebnum); if (err) return err; pr_info("writing 1st page of block %d\n", ebnum); prandom_bytes_state(&rnd_state, writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); - err = mtd_write(mtd, addr0, pgsize, &written, writebuf); - if (err || written != pgsize) { + err = mtdtest_write(mtd, addr0, pgsize, writebuf); + if (err) { pr_err("error: write failed at %#llx\n", (long long)addr0); - return err ? err : -1; + return err; } pr_info("erasing block %d\n", ebnum2); - err = erase_eraseblock(ebnum2); + err = mtdtest_erase_eraseblock(mtd, ebnum2); if (err) return err; pr_info("reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); - err = mtd_read(mtd, addr0, pgsize, &read, readbuf); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr0, pgsize, readbuf); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr0); - return err ? err : -1; + return err; } pr_info("verifying 1st page of block %d\n", ebnum); @@ -379,7 +325,6 @@ static int erasecrosstest(void) static int erasetest(void) { - size_t read, written; int err = 0, i, ebnum, ok = 1; loff_t addr0; @@ -393,32 +338,30 @@ static int erasetest(void) } pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); + err = mtdtest_erase_eraseblock(mtd, ebnum); if (err) return err; pr_info("writing 1st page of block %d\n", ebnum); prandom_bytes_state(&rnd_state, writebuf, pgsize); - err = mtd_write(mtd, addr0, pgsize, &written, writebuf); - if (err || written != pgsize) { + err = mtdtest_write(mtd, addr0, pgsize, writebuf); + if (err) { pr_err("error: write failed at %#llx\n", (long long)addr0); - return err ? err : -1; + return err; } pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); + err = mtdtest_erase_eraseblock(mtd, ebnum); if (err) return err; pr_info("reading 1st page of block %d\n", ebnum); - err = mtd_read(mtd, addr0, pgsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr0, pgsize, twopages); + if (err) { pr_err("error: read failed at %#llx\n", (long long)addr0); - return err ? err : -1; + return err; } pr_info("verifying 1st page of block %d is all 0xff\n", @@ -438,36 +381,6 @@ static int erasetest(void) return err; } -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_pagetest_init(void) { int err = 0; @@ -521,21 +434,19 @@ static int __init mtd_pagetest_init(void) if (!boundary) goto out; - err = scan_for_bad_eraseblocks(); + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; /* Erase all eraseblocks */ pr_info("erasing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - goto out; - cond_resched(); - } - pr_info("erased %u eraseblocks\n", i); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); + if (err) + goto out; + pr_info("erased %u eraseblocks\n", ebcnt); /* Write all eraseblocks */ prandom_seed_state(&rnd_state, 1); -- cgit v0.10.2 From bf9c223664e6f5fdf538919c715c45c26dbb4268 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:11 +0900 Subject: mtd: mtd_readtest: use mtd_test helpers Use mtdtest_read() and mtdtest_scan_for_bad_eraseblocks() in mtd_test helpers. Signed-off-by: Akinobu Mita Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/readtest.c b/drivers/mtd/tests/readtest.c index 2cdd0c4..ffd50d6 100644 --- a/drivers/mtd/tests/readtest.c +++ b/drivers/mtd/tests/readtest.c @@ -29,6 +29,8 @@ #include #include +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -44,7 +46,6 @@ static int pgcnt; static int read_eraseblock_by_page(int ebnum) { - size_t read; int i, ret, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; @@ -52,16 +53,12 @@ static int read_eraseblock_by_page(int ebnum) for (i = 0; i < pgcnt; i++) { memset(buf, 0 , pgsize); - ret = mtd_read(mtd, addr, pgsize, &read, buf); - if (ret == -EUCLEAN) - ret = 0; - if (ret || read != pgsize) { + ret = mtdtest_read(mtd, addr, pgsize, buf); + if (ret) { pr_err("error: read failed at %#llx\n", (long long)addr); if (!err) err = ret; - if (!err) - err = -EINVAL; } if (mtd->oobsize) { struct mtd_oob_ops ops; @@ -127,39 +124,6 @@ static void dump_eraseblock(int ebnum) } } -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - if (!mtd_can_have_bb(mtd)) - return 0; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_readtest_init(void) { uint64_t tmp; @@ -208,7 +172,10 @@ static int __init mtd_readtest_init(void) if (!iobuf1) goto out; - err = scan_for_bad_eraseblocks(); + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; -- cgit v0.10.2 From 59b0816d7c7de3c14d6dd59da3baa3deffb973da Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:12 +0900 Subject: mtd: mtd_speedtest: use mtd_test helpers Use mtdtest_write(), mtdtest_read(), mtdtest_scan_for_bad_eraseblocks(), mtdtest_erase_good_eraseblocks() in mtd_test helpers. Signed-off-by: Akinobu Mita Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c index 20b63d1..457c45c 100644 --- a/drivers/mtd/tests/speedtest.c +++ b/drivers/mtd/tests/speedtest.c @@ -30,6 +30,8 @@ #include #include +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -49,33 +51,6 @@ static int pgcnt; static int goodebcnt; static struct timeval start, finish; - -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - static int multiblock_erase(int ebnum, int blocks) { int err; @@ -103,52 +78,29 @@ static int multiblock_erase(int ebnum, int blocks) return 0; } -static int erase_whole_device(void) -{ - int err; - unsigned int i; - - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - return err; - cond_resched(); - } - return 0; -} - static int write_eraseblock(int ebnum) { - size_t written; - int err = 0; + int err; loff_t addr = ebnum * mtd->erasesize; - err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf); - if (err || written != mtd->erasesize) { + err = mtdtest_write(mtd, addr, mtd->erasesize, iobuf); + if (err) pr_err("error: write failed at %#llx\n", addr); - if (!err) - err = -EINVAL; - } return err; } static int write_eraseblock_by_page(int ebnum) { - size_t written; int i, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < pgcnt; i++) { - err = mtd_write(mtd, addr, pgsize, &written, buf); - if (err || written != pgsize) { + err = mtdtest_write(mtd, addr, pgsize, buf); + if (err) { pr_err("error: write failed at %#llx\n", addr); - if (!err) - err = -EINVAL; break; } addr += pgsize; @@ -160,30 +112,26 @@ static int write_eraseblock_by_page(int ebnum) static int write_eraseblock_by_2pages(int ebnum) { - size_t written, sz = pgsize * 2; + size_t sz = pgsize * 2; int i, n = pgcnt / 2, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < n; i++) { - err = mtd_write(mtd, addr, sz, &written, buf); - if (err || written != sz) { + err = mtdtest_write(mtd, addr, sz, buf); + if (err) { pr_err("error: write failed at %#llx\n", addr); - if (!err) - err = -EINVAL; return err; } addr += sz; buf += sz; } if (pgcnt % 2) { - err = mtd_write(mtd, addr, pgsize, &written, buf); - if (err || written != pgsize) { + err = mtdtest_write(mtd, addr, pgsize, buf); + if (err) { pr_err("error: write failed at %#llx\n", addr); - if (!err) - err = -EINVAL; } } @@ -192,40 +140,27 @@ static int write_eraseblock_by_2pages(int ebnum) static int read_eraseblock(int ebnum) { - size_t read; - int err = 0; + int err; loff_t addr = ebnum * mtd->erasesize; - err = mtd_read(mtd, addr, mtd->erasesize, &read, iobuf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != mtd->erasesize) { + err = mtdtest_read(mtd, addr, mtd->erasesize, iobuf); + if (err) pr_err("error: read failed at %#llx\n", addr); - if (!err) - err = -EINVAL; - } return err; } static int read_eraseblock_by_page(int ebnum) { - size_t read; int i, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < pgcnt; i++) { - err = mtd_read(mtd, addr, pgsize, &read, buf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr, pgsize, buf); + if (err) { pr_err("error: read failed at %#llx\n", addr); - if (!err) - err = -EINVAL; break; } addr += pgsize; @@ -237,53 +172,32 @@ static int read_eraseblock_by_page(int ebnum) static int read_eraseblock_by_2pages(int ebnum) { - size_t read, sz = pgsize * 2; + size_t sz = pgsize * 2; int i, n = pgcnt / 2, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < n; i++) { - err = mtd_read(mtd, addr, sz, &read, buf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != sz) { + err = mtdtest_read(mtd, addr, sz, buf); + if (err) { pr_err("error: read failed at %#llx\n", addr); - if (!err) - err = -EINVAL; return err; } addr += sz; buf += sz; } if (pgcnt % 2) { - err = mtd_read(mtd, addr, pgsize, &read, buf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { + err = mtdtest_read(mtd, addr, pgsize, buf); + if (err) { pr_err("error: read failed at %#llx\n", addr); - if (!err) - err = -EINVAL; } } return err; } -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - static inline void start_timing(void) { do_gettimeofday(&start); @@ -308,30 +222,6 @@ static long calc_speed(void) return k; } -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - if (!mtd_can_have_bb(mtd)) - goto out; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); -out: - goodebcnt = ebcnt - bad; - return 0; -} - static int __init mtd_speedtest_init(void) { int err, i, blocks, j, k; @@ -387,11 +277,18 @@ static int __init mtd_speedtest_init(void) prandom_bytes(iobuf, mtd->erasesize); - err = scan_for_bad_eraseblocks(); + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; + for (i = 0; i < ebcnt; i++) { + if (!bbt[i]) + goodebcnt++; + } - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -425,7 +322,7 @@ static int __init mtd_speedtest_init(void) speed = calc_speed(); pr_info("eraseblock read speed is %ld KiB/s\n", speed); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -459,7 +356,7 @@ static int __init mtd_speedtest_init(void) speed = calc_speed(); pr_info("page read speed is %ld KiB/s\n", speed); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -496,14 +393,9 @@ static int __init mtd_speedtest_init(void) /* Erase all eraseblocks */ pr_info("Testing erase speed\n"); start_timing(); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - goto out; - cond_resched(); - } + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); + if (err) + goto out; stop_timing(); speed = calc_speed(); pr_info("erase speed is %ld KiB/s\n", speed); -- cgit v0.10.2 From 5a78df69eeb34b560a92f64170625083018383de Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:13 +0900 Subject: mtd: mtd_stresstest: use mtd_test helpers Use mtdtest_read(), mtdtest_write(), mtdtest_erase_eraseblock(), and mtdtest_scan_for_bad_eraseblocks() in mtd_test helpers. Signed-off-by: Akinobu Mita Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/stresstest.c b/drivers/mtd/tests/stresstest.c index 3a95e61..1695248 100644 --- a/drivers/mtd/tests/stresstest.c +++ b/drivers/mtd/tests/stresstest.c @@ -31,6 +31,8 @@ #include #include +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -81,46 +83,8 @@ static int rand_len(int offs) return len; } -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (unlikely(err)) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (unlikely(ei.state == MTD_ERASE_FAILED)) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - static int do_read(void) { - size_t read; int eb = rand_eb(); int offs = rand_offs(); int len = rand_len(offs), err; @@ -133,14 +97,10 @@ static int do_read(void) len = mtd->erasesize - offs; } addr = eb * mtd->erasesize + offs; - err = mtd_read(mtd, addr, len, &read, readbuf); - if (mtd_is_bitflip(err)) - err = 0; - if (unlikely(err || read != len)) { + err = mtdtest_read(mtd, addr, len, readbuf); + if (unlikely(err)) { pr_err("error: read failed at 0x%llx\n", (long long)addr); - if (!err) - err = -EINVAL; return err; } return 0; @@ -149,12 +109,11 @@ static int do_read(void) static int do_write(void) { int eb = rand_eb(), offs, err, len; - size_t written; loff_t addr; offs = offsets[eb]; if (offs >= mtd->erasesize) { - err = erase_eraseblock(eb); + err = mtdtest_erase_eraseblock(mtd, eb); if (err) return err; offs = offsets[eb] = 0; @@ -165,19 +124,17 @@ static int do_write(void) if (bbt[eb + 1]) len = mtd->erasesize - offs; else { - err = erase_eraseblock(eb + 1); + err = mtdtest_erase_eraseblock(mtd, eb + 1); if (err) return err; offsets[eb + 1] = 0; } } addr = eb * mtd->erasesize + offs; - err = mtd_write(mtd, addr, len, &written, writebuf); - if (unlikely(err || written != len)) { + err = mtdtest_write(mtd, addr, len, writebuf); + if (unlikely(err)) { pr_err("error: write failed at 0x%llx\n", (long long)addr); - if (!err) - err = -EINVAL; return err; } offs += len; @@ -197,28 +154,6 @@ static int do_operation(void) return do_write(); } -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - if (!mtd_can_have_bb(mtd)) - return 0; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_stresstest_init(void) { int err; @@ -280,7 +215,10 @@ static int __init mtd_stresstest_init(void) offsets[i] = mtd->erasesize; prandom_bytes(writebuf, bufsize); - err = scan_for_bad_eraseblocks(); + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; -- cgit v0.10.2 From 725cd71c0245d1ac7e4441799e2d32fe4b68be8f Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:14 +0900 Subject: mtd: mtd_subpagetest: use mtd_test helpers Use mtdtest_scan_for_bad_eraseblocks() and mtdtest_erase_good_eraseblocks() in mtd_test helpers. Signed-off-by: Akinobu Mita Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/subpagetest.c b/drivers/mtd/tests/subpagetest.c index e41a04f..e2c0adf 100644 --- a/drivers/mtd/tests/subpagetest.c +++ b/drivers/mtd/tests/subpagetest.c @@ -30,6 +30,8 @@ #include #include +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -51,50 +53,6 @@ static inline void clear_data(unsigned char *buf, size_t len) memset(buf, 0, len); } -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -static int erase_whole_device(void) -{ - int err; - unsigned int i; - - pr_info("erasing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - return err; - cond_resched(); - } - pr_info("erased %u eraseblocks\n", i); - return 0; -} - static int write_eraseblock(int ebnum) { size_t written; @@ -317,36 +275,6 @@ static int verify_all_eraseblocks_ff(void) return 0; } -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_subpagetest_init(void) { int err = 0; @@ -396,12 +324,15 @@ static int __init mtd_subpagetest_init(void) readbuf = kmalloc(bufsize, GFP_KERNEL); if (!readbuf) goto out; + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; - err = scan_for_bad_eraseblocks(); + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -433,7 +364,7 @@ static int __init mtd_subpagetest_init(void) } pr_info("verified %u eraseblocks\n", i); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -471,7 +402,7 @@ static int __init mtd_subpagetest_init(void) } pr_info("verified %u eraseblocks\n", i); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; -- cgit v0.10.2 From 6cf78358c94fc30cc1fe93992b67060302a2457e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:15 +0900 Subject: mtd: mtd_torturetest: use mtd_test helpers Use mtdtest_scan_for_bad_eraseblocks() and mtdtest_erase_good_eraseblocks() in mtd_test helpers. Signed-off-by: Akinobu Mita Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/torturetest.c b/drivers/mtd/tests/torturetest.c index 3a9f6a6..eeab969 100644 --- a/drivers/mtd/tests/torturetest.c +++ b/drivers/mtd/tests/torturetest.c @@ -32,6 +32,7 @@ #include #include #include +#include "mtd_test.h" #define RETRIES 3 @@ -93,35 +94,6 @@ static inline void stop_timing(void) } /* - * Erase eraseblock number @ebnum. - */ -static inline int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -/* * Check that the contents of eraseblock number @enbum is equivalent to the * @buf buffer. */ @@ -208,7 +180,7 @@ static inline int write_pattern(int ebnum, void *buf) static int __init tort_init(void) { int err = 0, i, infinite = !cycles_count; - int *bad_ebs; + unsigned char *bad_ebs; printk(KERN_INFO "\n"); printk(KERN_INFO "=================================================\n"); @@ -265,7 +237,7 @@ static int __init tort_init(void) if (!check_buf) goto out_patt_FF; - bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL); + bad_ebs = kzalloc(ebcnt, GFP_KERNEL); if (!bad_ebs) goto out_check_buf; @@ -283,40 +255,16 @@ static int __init tort_init(void) } } - /* - * Check if there is a bad eraseblock among those we are going to test. - */ - if (mtd_can_have_bb(mtd)) { - for (i = eb; i < eb + ebcnt; i++) { - err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); - - if (err < 0) { - pr_info("block_isbad() returned %d " - "for EB %d\n", err, i); - goto out; - } - - if (err) { - pr_err("EB %d is bad. Skip it.\n", i); - bad_ebs[i - eb] = 1; - } - } - } + err = mtdtest_scan_for_bad_eraseblocks(mtd, bad_ebs, eb, ebcnt); + if (err) + goto out; start_timing(); while (1) { int i; void *patt; - /* Erase all eraseblocks */ - for (i = eb; i < eb + ebcnt; i++) { - if (bad_ebs[i - eb]) - continue; - err = erase_eraseblock(i); - if (err) - goto out; - cond_resched(); - } + mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt); /* Check if the eraseblocks contain only 0xFF bytes */ if (check) { -- cgit v0.10.2 From 561775169ce289850670fdb4a7353b67a7095bfa Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 3 Aug 2013 18:52:16 +0900 Subject: mtd: mtd_nandbiterrs: use mtd_test helpers Use mtdtest_write() and mtdtest_erase_eraseblock() in mtd_test helpers. Signed-off-by: Akinobu Mita Cc: Iwo Mergler Cc: Brian Norris Cc: Vikram Narayanan Cc: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/nandbiterrs.c b/drivers/mtd/tests/nandbiterrs.c index 207bf9a..5a8c858 100644 --- a/drivers/mtd/tests/nandbiterrs.c +++ b/drivers/mtd/tests/nandbiterrs.c @@ -49,6 +49,7 @@ #include #include #include +#include "mtd_test.h" static int dev; module_param(dev, int, S_IRUGO); @@ -98,45 +99,17 @@ static uint8_t hash(unsigned offset) return c; } -static int erase_block(void) -{ - int err; - struct erase_info ei; - loff_t addr = eraseblock * mtd->erasesize; - - pr_info("erase_block\n"); - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err || ei.state == MTD_ERASE_FAILED) { - pr_err("error %d while erasing\n", err); - if (!err) - err = -EIO; - return err; - } - - return 0; -} - /* Writes wbuffer to page */ static int write_page(int log) { - int err = 0; - size_t written; + int err; if (log) pr_info("write_page\n"); - err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer); - if (err || written != mtd->writesize) { + err = mtdtest_write(mtd, offset, mtd->writesize, wbuffer); + if (err) pr_err("error: write failed at %#llx\n", (long long)offset); - if (!err) - err = -EIO; - } return err; } @@ -415,7 +388,7 @@ static int __init mtd_nandbiterrs_init(void) goto exit_rbuffer; } - err = erase_block(); + err = mtdtest_erase_eraseblock(mtd, eraseblock); if (err) goto exit_error; @@ -428,7 +401,7 @@ static int __init mtd_nandbiterrs_init(void) goto exit_error; /* We leave the block un-erased in case of test failure. */ - err = erase_block(); + err = mtdtest_erase_eraseblock(mtd, eraseblock); if (err) goto exit_error; -- cgit v0.10.2 From 9fe5f52c9eb2f2df2b6ffdab0100f4475fef2ba1 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 7 Aug 2013 11:36:09 +0800 Subject: mtd: atmel_nand: fix the warning when CONFIG_OF is not defined This patch fix following warning: drivers/mtd/nand/atmel_nand.c:2007: warning: 'atmel_nand_nfc_match' defined but not used This patch add '#if defined(CONFIG_OF)' block to guard around the definition of atmel_nand_nfc_match, in order to avoid the warning when the kernel is configured without DT support. Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 65b302c..ddcd575 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -2251,10 +2251,12 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev) return 0; } +#if defined(CONFIG_OF) static struct of_device_id atmel_nand_nfc_match[] = { { .compatible = "atmel,sama5d3-nfc" }, { /* sentinel */ } }; +#endif static struct platform_driver atmel_nand_nfc_driver = { .driver = { -- cgit v0.10.2 From 52a073bd2c2b005250c4254b747d5ad9b2a089a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Wed, 7 Aug 2013 08:15:38 +0200 Subject: mtd: nand: gpmi-nand: use more sensible error codes at various places MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Huang Shijie Signed-off-by: Lothar Waßmann Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index bcf9bc5..76d2f41 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -354,7 +354,7 @@ static int acquire_register_block(struct gpmi_nand_data *this, r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); if (!r) { pr_err("Can't get resource for %s\n", res_name); - return -ENXIO; + return -ENODEV; } p = ioremap(r->start, resource_size(r)); @@ -395,7 +395,7 @@ static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h) r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name); if (!r) { pr_err("Can't get resource for %s\n", res_name); - return -ENXIO; + return -ENODEV; } err = request_irq(r->start, irq_h, 0, res_name, this); @@ -1584,7 +1584,7 @@ static int gpmi_nand_probe(struct platform_device *pdev) pdev->id_entry = of_id->data; } else { pr_err("Failed to find the right device id.\n"); - return -ENOMEM; + return -ENODEV; } this = kzalloc(sizeof(*this), GFP_KERNEL); -- cgit v0.10.2 From 07cc0fbadb8bfcbba57c9035beece608ceee0bd3 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sat, 27 Jul 2013 23:09:53 +0900 Subject: mtd: denali: use NAND_CI_CELLTYPE_MSK instead of hardcoded constant Use NAND_CI_CELLTYPE_MSK to extract the cell type from nand_chip.cellinfo instead of hardcoded constant. Signed-off-by: Akinobu Mita Cc: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 0c8bb6b..2ed2bb3 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1520,7 +1520,7 @@ int denali_init(struct denali_nand_info *denali) * so just let controller do 15bit ECC for MLC and 8bit ECC for * SLC if possible. * */ - if (denali->nand.cellinfo & 0xc && + if (denali->nand.cellinfo & NAND_CI_CELLTYPE_MSK && (denali->mtd.oobsize > (denali->bbtskipbytes + ECC_15BITS * (denali->mtd.writesize / ECC_SECTOR_SIZE)))) { -- cgit v0.10.2 From 0278fd3fa34fb029e40224dd83d2628254f45481 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:17:44 +0900 Subject: mtd: devices: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 57522c8..b5190c4 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -929,7 +929,7 @@ static int m25p_probe(struct spi_device *spi) * a chip ID, try the JEDEC id commands; they'll work for most * newer chips, even if we don't recognize the particular chip. */ - data = spi->dev.platform_data; + data = dev_get_platdata(&spi->dev); if (data && data->type) { const struct spi_device_id *plat_id; diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 4a43664..0e8cbfe 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -622,7 +622,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, struct dataflash *priv; struct mtd_info *device; struct mtd_part_parser_data ppdata; - struct flash_platform_data *pdata = spi->dev.platform_data; + struct flash_platform_data *pdata = dev_get_platdata(&spi->dev); char *otp_tag = ""; int err = 0; diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index 38b5979..a42f1f0 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -372,7 +372,7 @@ static int sst25l_probe(struct spi_device *spi) mutex_init(&flash->lock); spi_set_drvdata(spi, flash); - data = spi->dev.platform_data; + data = dev_get_platdata(&spi->dev); if (data && data->name) flash->mtd.name = data->name; else -- cgit v0.10.2 From d20d5a5780a014bc4d24dbcb6daf7673a9dddf98 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:18:06 +0900 Subject: mtd: maps: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index 319b04a..5434d8d 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -128,7 +128,7 @@ static const char * const part_probe_types[] = { static int bfin_flash_probe(struct platform_device *pdev) { int ret; - struct physmap_flash_data *pdata = pdev->dev.platform_data; + struct physmap_flash_data *pdata = dev_get_platdata(&pdev->dev); struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1); struct async_state *state; diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c index 5ede282..1adba86 100644 --- a/drivers/mtd/maps/gpio-addr-flash.c +++ b/drivers/mtd/maps/gpio-addr-flash.c @@ -196,7 +196,7 @@ static int gpio_flash_probe(struct platform_device *pdev) struct resource *gpios; struct async_state *state; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0); diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 09c542b..10debfe 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -152,7 +152,7 @@ static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL }; static int ixp4xx_flash_remove(struct platform_device *dev) { - struct flash_platform_data *plat = dev->dev.platform_data; + struct flash_platform_data *plat = dev_get_platdata(&dev->dev); struct ixp4xx_flash_info *info = platform_get_drvdata(dev); if(!info) @@ -178,7 +178,7 @@ static int ixp4xx_flash_remove(struct platform_device *dev) static int ixp4xx_flash_probe(struct platform_device *dev) { - struct flash_platform_data *plat = dev->dev.platform_data; + struct flash_platform_data *plat = dev_get_platdata(&dev->dev); struct ixp4xx_flash_info *info; struct mtd_part_parser_data ppdata = { .origin = dev->resource->start, diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c index 675ccb8..98bb5d5 100644 --- a/drivers/mtd/maps/latch-addr-flash.c +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -103,7 +103,7 @@ static int latch_addr_flash_remove(struct platform_device *dev) if (info == NULL) return 0; - latch_addr_data = dev->dev.platform_data; + latch_addr_data = dev_get_platdata(&dev->dev); if (info->mtd != NULL) { mtd_device_unregister(info->mtd); @@ -134,7 +134,7 @@ static int latch_addr_flash_probe(struct platform_device *dev) int chipsel; int err; - latch_addr_data = dev->dev.platform_data; + latch_addr_data = dev_get_platdata(&dev->dev); if (latch_addr_data == NULL) return -ENODEV; diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 9eb7ead..f73cd46 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -41,7 +41,7 @@ static int physmap_flash_remove(struct platform_device *dev) if (info == NULL) return 0; - physmap_data = dev->dev.platform_data; + physmap_data = dev_get_platdata(&dev->dev); if (info->cmtd) { mtd_device_unregister(info->cmtd); @@ -68,7 +68,7 @@ static void physmap_set_vpp(struct map_info *map, int state) unsigned long flags; pdev = (struct platform_device *)map->map_priv_1; - physmap_data = pdev->dev.platform_data; + physmap_data = dev_get_platdata(&pdev->dev); if (!physmap_data->set_vpp) return; @@ -102,7 +102,7 @@ static int physmap_flash_probe(struct platform_device *dev) int i; int devices_found = 0; - physmap_data = dev->dev.platform_data; + physmap_data = dev_get_platdata(&dev->dev); if (physmap_data == NULL) return -ENODEV; diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index b0b85eb..6762716 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -128,13 +128,13 @@ static int platram_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "probe entered\n"); - if (pdev->dev.platform_data == NULL) { + if (dev_get_platdata(&pdev->dev) == NULL) { dev_err(&pdev->dev, "no platform data supplied\n"); err = -ENOENT; goto exit_error; } - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 22c55cb..d210d13 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -49,7 +49,7 @@ static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL }; static int pxa2xx_flash_probe(struct platform_device *pdev) { - struct flash_platform_data *flash = pdev->dev.platform_data; + struct flash_platform_data *flash = dev_get_platdata(&pdev->dev); struct pxa2xx_flash_info *info; struct resource *res; diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index ab4ec55..9352512 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c @@ -36,7 +36,7 @@ static int rbtx4939_flash_remove(struct platform_device *dev) return 0; if (info->mtd) { - struct rbtx4939_flash_data *pdata = dev->dev.platform_data; + struct rbtx4939_flash_data *pdata = dev_get_platdata(&dev->dev); mtd_device_unregister(info->mtd); map_destroy(info->mtd); @@ -56,7 +56,7 @@ static int rbtx4939_flash_probe(struct platform_device *dev) int err = 0; unsigned long size; - pdata = dev->dev.platform_data; + pdata = dev_get_platdata(&dev->dev); if (!pdata) return -ENODEV; diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 9e70f8e..8fc06bf 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -248,7 +248,7 @@ static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL }; static int sa1100_mtd_probe(struct platform_device *pdev) { - struct flash_platform_data *plat = pdev->dev.platform_data; + struct flash_platform_data *plat = dev_get_platdata(&pdev->dev); struct sa_info *info; int err; @@ -277,7 +277,7 @@ static int sa1100_mtd_probe(struct platform_device *pdev) static int __exit sa1100_mtd_remove(struct platform_device *pdev) { struct sa_info *info = platform_get_drvdata(pdev); - struct flash_platform_data *plat = pdev->dev.platform_data; + struct flash_platform_data *plat = dev_get_platdata(&pdev->dev); sa1100_destroy(info, plat); -- cgit v0.10.2 From 453810b79571ff6622a481f0556fc4972f87a24f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:18:33 +0900 Subject: mtd: nand: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index ddcd575..1ffa52f 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -2023,7 +2023,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if (res) goto err_nand_ioremap; } else { - memcpy(&host->board, pdev->dev.platform_data, + memcpy(&host->board, dev_get_platdata(&pdev->dev), sizeof(struct atmel_nand_data)); } diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 217459d..ae8dd7c 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -411,7 +411,7 @@ static int au1550nd_probe(struct platform_device *pdev) struct resource *r; int ret, cs; - pd = pdev->dev.platform_data; + pd = dev_get_platdata(&pdev->dev); if (!pd) { dev_err(&pdev->dev, "missing platform data\n"); return -ENODEV; diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 809a63c..2c42e12 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -171,7 +171,7 @@ static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev) static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev) { - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } /* diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 999ad9c..b77a01e 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -530,7 +530,7 @@ MODULE_DEVICE_TABLE(of, davinci_nand_of_match); static struct davinci_nand_pdata *nand_davinci_get_pdata(struct platform_device *pdev) { - if (!pdev->dev.platform_data && pdev->dev.of_node) { + if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) { struct davinci_nand_pdata *pdata; const char *mode; u32 prop; @@ -575,13 +575,13 @@ static struct davinci_nand_pdata pdata->bbt_options = NAND_BBT_USE_FLASH; } - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } #else static struct davinci_nand_pdata *nand_davinci_get_pdata(struct platform_device *pdev) { - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } #endif diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 800a1cc..e826f89 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -167,8 +167,8 @@ static inline int gpio_nand_get_config(const struct device *dev, if (!ret) return ret; - if (dev->platform_data) { - memcpy(plat, dev->platform_data, sizeof(*plat)); + if (dev_get_platdata(dev)) { + memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); return 0; } @@ -207,7 +207,7 @@ static int gpio_nand_probe(struct platform_device *pdev) struct mtd_part_parser_data ppdata = {}; int ret = 0; - if (!pdev->dev.of_node && !pdev->dev.platform_data) + if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev)) return -EINVAL; gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL); diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index 698a4d1..a264b88 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -411,7 +411,7 @@ static int jz_nand_probe(struct platform_device *pdev) struct jz_nand *nand; struct nand_chip *chip; struct mtd_info *mtd; - struct jz_nand_platform_data *pdata = pdev->dev.platform_data; + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); size_t chipnr, bank_idx; uint8_t nand_maf_id = 0, nand_dev_id = 0; @@ -548,7 +548,7 @@ err_free: static int jz_nand_remove(struct platform_device *pdev) { struct jz_nand *nand = platform_get_drvdata(pdev); - struct jz_nand_platform_data *pdata = pdev->dev.platform_data; + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); size_t i; nand_release(&nand->mtd); diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 74a469b..f4dd2a8 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -696,7 +696,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } lpc32xx_wp_disable(host); - host->pdata = pdev->dev.platform_data; + host->pdata = dev_get_platdata(&pdev->dev); nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index a66f33f..add7570 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -798,7 +798,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } lpc32xx_wp_disable(host); - host->pdata = pdev->dev.platform_data; + host->pdata = dev_get_platdata(&pdev->dev); mtd = &host->mtd; chip = &host->nand_chip; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 0581bd3..a99ff1d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1432,7 +1432,8 @@ static int mxcnd_probe(struct platform_device *pdev) err = mxcnd_probe_dt(host); if (err > 0) { - struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; + struct mxc_nand_platform_data *pdata = + dev_get_platdata(&pdev->dev); if (pdata) { host->pdata = *pdata; host->devtype_data = (struct mxc_nand_devtype_data *) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index cb40f87..4ecf0e5 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1831,7 +1831,7 @@ static int omap_nand_probe(struct platform_device *pdev) struct resource *res; struct mtd_part_parser_data ppdata = {}; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (pdata == NULL) { dev_err(&pdev->dev, "platform data missing\n"); return -ENODEV; diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 46f308d..a393a5b 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -130,8 +130,9 @@ static int __init orion_nand_probe(struct platform_device *pdev) if (!of_property_read_u32(pdev->dev.of_node, "chip-delay", &val)) board->chip_delay = (u8)val; - } else - board = pdev->dev.platform_data; + } else { + board = dev_get_platdata(&pdev->dev); + } mtd->priv = nc; mtd->owner = THIS_MODULE; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index d42700a..cad4cdc 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -30,7 +30,7 @@ static const char *part_probe_types[] = { "cmdlinepart", NULL }; */ static int plat_nand_probe(struct platform_device *pdev) { - struct platform_nand_data *pdata = pdev->dev.platform_data; + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); struct mtd_part_parser_data ppdata; struct plat_nand_data *data; struct resource *res; @@ -136,7 +136,7 @@ out_free: static int plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); - struct platform_nand_data *pdata = pdev->dev.platform_data; + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index fdc045d..501e380 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -815,7 +815,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, const struct pxa3xx_nand_flash *f) { struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); struct pxa3xx_nand_host *host = info->host[info->cs]; uint32_t ndcr = 0x0; /* enable all interrupts */ @@ -958,7 +958,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) struct pxa3xx_nand_host *host = mtd->priv; struct pxa3xx_nand_info *info = host->info_data; struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL; const struct pxa3xx_nand_flash *f = NULL; struct nand_chip *chip = mtd->priv; @@ -1058,7 +1058,7 @@ static int alloc_nand_resource(struct platform_device *pdev) struct resource *r; int ret, irq, cs; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) + sizeof(*host)) * pdata->num_cs, GFP_KERNEL); if (!info) @@ -1176,7 +1176,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) if (!info) return 0; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); irq = platform_get_irq(pdev, 0); if (irq >= 0) @@ -1239,7 +1239,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) if (ret) return ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no platform data defined\n"); return -ENODEV; @@ -1286,7 +1286,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) struct mtd_info *mtd; int cs; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (info->state) { dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); return -EAGAIN; @@ -1307,7 +1307,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev) struct mtd_info *mtd; int cs; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); /* We don't want to handle interrupt without calling mtd routine */ disable_int(info, NDCR_INT_MASK); diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 191823e..d65cbe9 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -150,7 +150,7 @@ static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) { - return dev->dev.platform_data; + return dev_get_platdata(&dev->dev); } static inline int allow_clk_suspend(struct s3c2410_nand_info *info) diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index e57e18e..a3c84eb 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -137,7 +137,7 @@ static void flctl_setup_dma(struct sh_flctl *flctl) dma_cap_mask_t mask; struct dma_slave_config cfg; struct platform_device *pdev = flctl->pdev; - struct sh_flctl_platform_data *pdata = pdev->dev.platform_data; + struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret; if (!pdata) @@ -1131,7 +1131,7 @@ static int flctl_probe(struct platform_device *pdev) if (pdev->dev.of_node) pdata = flctl_parse_dt(&pdev->dev); else - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no setup data defined\n"); diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 1d41578..87908d7 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -112,7 +112,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev) struct resource *r; int err = 0; struct sharpsl_nand *sharpsl; - struct sharpsl_nand_platform_data *data = pdev->dev.platform_data; + struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev); if (!data) { dev_err(&pdev->dev, "no platform data!\n"); diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 508e9e0..396530d 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -357,7 +357,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_probe(struct platform_device *dev) { - struct tmio_nand_data *data = dev->dev.platform_data; + struct tmio_nand_data *data = dev_get_platdata(&dev->dev); struct resource *fcr = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *ccr = platform_get_resource(dev, diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 0488dd5..440b570 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -87,7 +87,7 @@ static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) { struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); return drvdata->base + (reg << plat->shift); } @@ -138,7 +138,7 @@ static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, struct nand_chip *chip = mtd->priv; struct txx9ndfmc_priv *txx9_priv = chip->priv; struct platform_device *dev = txx9_priv->dev; - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); if (ctrl & NAND_CTRL_CHANGE) { u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); @@ -225,7 +225,7 @@ static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) static void txx9ndfmc_initialize(struct platform_device *dev) { - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int tmout = 100; @@ -274,7 +274,7 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd) static int __init txx9ndfmc_probe(struct platform_device *dev) { - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); int hold, spw; int i; struct txx9ndfmc_drvdata *drvdata; -- cgit v0.10.2 From e09f7f992833ed73f1afc5eaa7cc4d5b10b04b7c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 30 Jul 2013 17:18:53 +0900 Subject: mtd: onenand: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 42520a3..63699ff 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -38,7 +38,7 @@ struct onenand_info { static int generic_onenand_probe(struct platform_device *pdev) { struct onenand_info *info; - struct onenand_platform_data *pdata = pdev->dev.platform_data; + struct onenand_platform_data *pdata = dev_get_platdata(&pdev->dev); struct resource *res = pdev->resource; unsigned long size = resource_size(res); int err; diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index cd43cea..558071b 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -639,7 +639,7 @@ static int omap2_onenand_probe(struct platform_device *pdev) struct resource *res; struct mtd_part_parser_data ppdata = {}; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (pdata == NULL) { dev_err(&pdev->dev, "platform data missing\n"); return -ENODEV; diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index cebc48d..df7400d 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -867,7 +867,7 @@ static int s3c_onenand_probe(struct platform_device *pdev) struct resource *r; int size, err; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); /* No need to check pdata. the platform data is optional */ size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); -- cgit v0.10.2 From 1fad0e8b9a8889f6ca79be570e06d00785f15aed Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 7 Aug 2013 17:58:11 +0800 Subject: mtd: atmel_nand: move the sanity check to the beginning of pmecc_enable() It is better to do the sanity check for the parameter before any hardware operation. Signed-off-by: Josh Wu Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 1ffa52f..0e365da 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -905,15 +905,15 @@ static void pmecc_enable(struct atmel_nand_host *host, int ecc_op) { u32 val; - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); - val = pmecc_readl_relaxed(host->ecc, CFG); - if (ecc_op != NAND_ECC_READ && ecc_op != NAND_ECC_WRITE) { dev_err(host->dev, "atmel_nand: wrong pmecc operation type!"); return; } + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); + val = pmecc_readl_relaxed(host->ecc, CFG); + if (ecc_op == NAND_ECC_READ) pmecc_writel(host->ecc, CFG, (val & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE); -- cgit v0.10.2 From 4cfeca2d58de8292f45351f84e84f657222a4611 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 May 2013 11:17:25 +0800 Subject: mtd: add datasheet's ECC information to nand_chip{} 1.) Why add the ECC information to the nand_chip{} ? Each nand chip has its requirement for the ECC correctability, such as "4bit ECC for each 512Byte" or "40bit ECC for each 1024Byte". This ECC info is very important to the nand controller, such as gpmi. Take the Micron MT29F64G08CBABA for example, its geometry is 8KiB page size, 744 bytes oob size and it requires 40bit ECC per 1KiB. If we do not provide the ECC info to the gpmi nand driver, it has to calculate the ECC correctability itself. The gpmi driver will gets the 56bit ECC for per 1KiB which is beyond its BCH's 40bit ecc capibility. The gpmi will quits in this case. But in actually, the gpmi can supports this nand chip if it can get the right ECC info. 2.) about the new fields. The @ecc_strength_ds stands for the ecc bits needed within the @ecc_step_ds. The two fields should be set from the nand chip's datasheets. For example: "4bit ECC for each 512Byte" could be: @ecc_strength_ds = 4, @ecc_step_ds = 512. "40bit ECC for each 1024Byte" could be: @ecc_strength_ds = 40, @ecc_step_ds = 1024. 3.) Why do not re-use the @strength and @size in the nand_ecc_ctrl{}? The @strength and @size in nand_ecc_ctrl{} is used by the nand controller driver, while the @ecc_strength_ds and @ecc_step_ds are get from the datasheet. Signed-off-by: Huang Shijie Reviewed-and-tested-by: Brian Norris Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 9f7b248..fbf3bba 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -437,6 +437,12 @@ struct nand_buffers { * bad block marker position; i.e., BBM == 11110111b is * not bad when badblockbits == 7 * @cellinfo: [INTERN] MLC/multichip data from chip ident + * @ecc_strength_ds: [INTERN] ECC correctability from the datasheet. + * Minimum amount of bit errors per @ecc_step_ds guaranteed + * to be correctable. If unknown, set to zero. + * @ecc_step_ds: [INTERN] ECC step required by the @ecc_strength_ds, + * also from the datasheet. It is the recommended ECC step + * size, if known; if unknown, set to zero. * @numchips: [INTERN] number of physical chips * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 @@ -513,6 +519,8 @@ struct nand_chip { unsigned int pagebuf_bitflips; int subpagesize; uint8_t cellinfo; + uint16_t ecc_strength_ds; + uint16_t ecc_step_ds; int badblockpos; int badblockbits; -- cgit v0.10.2 From 10c86babfb21a07272495a968114f677313bea51 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 May 2013 11:17:26 +0800 Subject: mtd: get the ECC info from the parameter page for ONFI nand From the ONFI spec, we can just get the ECC info from the @ecc_bits field of the parameter page. Signed-off-by: Huang Shijie Reviewed-and-tested-by: Brian Norris Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9a48758..42d44bb 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2913,6 +2913,11 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, if (le16_to_cpu(p->features) & 1) *busw = NAND_BUSWIDTH_16; + if (p->ecc_bits != 0xff) { + chip->ecc_strength_ds = p->ecc_bits; + chip->ecc_step_ds = 512; + } + pr_info("ONFI flash detected\n"); return 1; } -- cgit v0.10.2 From 5138a98f6f47ce62a86cce4b9c381aaf4ad3a643 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 May 2013 11:17:27 +0800 Subject: mtd: add data structures for Extended Parameter Page Since the ONFI 2.1, the onfi spec adds the Extended Parameter Page to store the ECC info. The onfi spec tells us that if the nand chip's recommended ECC codeword size is not 512 bytes, then the @ecc_bits is 0xff. The host _SHOULD_ then read the Extended ECC information that is part of the extended parameter page to retrieve the ECC requirements for this device. This patch adds [1] the neccessary fields for nand_onfi_params{}, [2] and adds the onfi_ext_ecc_info{} for Extended ECC information, [3] adds onfi_ext_section{} for extended sections, [4] and adds onfi_ext_param_page{} for the Extended Parameter Page. Acked-by: Pekon Gupta Signed-off-by: Huang Shijie Reviewed-and-tested-by: Brian Norris [Brian: amended for checkpatch.pl] Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index fbf3bba..01a7f3c 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -227,7 +227,10 @@ struct nand_onfi_params { __le16 revision; __le16 features; __le16 opt_cmd; - u8 reserved[22]; + u8 reserved0[2]; + __le16 ext_param_page_length; /* since ONFI 2.1 */ + u8 num_of_param_pages; /* since ONFI 2.1 */ + u8 reserved1[17]; /* manufacturer information block */ char manufacturer[12]; @@ -284,6 +287,40 @@ struct nand_onfi_params { #define ONFI_CRC_BASE 0x4F4E +/* Extended ECC information Block Definition (since ONFI 2.1) */ +struct onfi_ext_ecc_info { + u8 ecc_bits; + u8 codeword_size; + __le16 bb_per_lun; + __le16 block_endurance; + u8 reserved[2]; +} __packed; + +#define ONFI_SECTION_TYPE_0 0 /* Unused section. */ +#define ONFI_SECTION_TYPE_1 1 /* for additional sections. */ +#define ONFI_SECTION_TYPE_2 2 /* for ECC information. */ +struct onfi_ext_section { + u8 type; + u8 length; +} __packed; + +#define ONFI_EXT_SECTION_MAX 8 + +/* Extended Parameter Page Definition (since ONFI 2.1) */ +struct onfi_ext_param_page { + __le16 crc; + u8 sig[4]; /* 'E' 'P' 'P' 'S' */ + u8 reserved0[10]; + struct onfi_ext_section sections[ONFI_EXT_SECTION_MAX]; + + /* + * The actual size of the Extended Parameter Page is in + * @ext_param_page_length of nand_onfi_params{}. + * The following are the variable length sections. + * So we do not add any fields below. Please see the ONFI spec. + */ +} __packed; + /** * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices * @lock: protection lock -- cgit v0.10.2 From 5b40db68cb1bd14b3d5b159754c9c1bc3bdeb080 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 May 2013 11:17:28 +0800 Subject: mtd: add a helper to get the supported features for ONFI nand add a helper to get the supported features for ONFI nand. Also add the neccessary macros. Signed-off-by: Huang Shijie Reviewed-and-tested-by: Brian Norris Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 01a7f3c..bf4c566 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -202,6 +202,10 @@ typedef enum { /* Keep gcc happy */ struct nand_chip; +/* ONFI features */ +#define ONFI_FEATURE_16_BIT_BUS (1 << 0) +#define ONFI_FEATURE_EXT_PARAM_PAGE (1 << 7) + /* ONFI timing mode, used in both asynchronous and synchronous mode */ #define ONFI_TIMING_MODE_0 (1 << 0) #define ONFI_TIMING_MODE_1 (1 << 1) @@ -756,6 +760,12 @@ struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd) return chip->priv; } +/* return the supported features. */ +static inline int onfi_feature(struct nand_chip *chip) +{ + return chip->onfi_version ? le16_to_cpu(chip->onfi_params.features) : 0; +} + /* return the supported asynchronous timing mode. */ static inline int onfi_get_async_timing_mode(struct nand_chip *chip) { -- cgit v0.10.2 From 6dcbe0cdd83fb5f77be4f44c9e06c535281c375a Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 22 May 2013 10:28:27 +0800 Subject: mtd: get the ECC info from the Extended Parameter Page Since the ONFI 2.1, the onfi spec adds the Extended Parameter Page to store the ECC info. The onfi spec tells us that if the nand chip's recommended ECC codeword size is not 512 bytes, then the @ecc_bits is 0xff. The host _SHOULD_ then read the Extended ECC information that is part of the extended parameter page to retrieve the ECC requirements for this device. This patch implement the reading of the Extended Parameter Page, and parses the sections for ECC type, and get the ECC info from the ECC section. Tested this patch with Micron MT29F64G08CBABAWP. Acked-by: Pekon Gupta Signed-off-by: Huang Shijie Reviewed-and-tested-by: Brian Norris Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 42d44bb..d8c723c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2848,6 +2848,78 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) return crc; } +/* Parse the Extended Parameter Page. */ +static int nand_flash_detect_ext_param_page(struct mtd_info *mtd, + struct nand_chip *chip, struct nand_onfi_params *p) +{ + struct onfi_ext_param_page *ep; + struct onfi_ext_section *s; + struct onfi_ext_ecc_info *ecc; + uint8_t *cursor; + int ret = -EINVAL; + int len; + int i; + + len = le16_to_cpu(p->ext_param_page_length) * 16; + ep = kmalloc(len, GFP_KERNEL); + if (!ep) { + ret = -ENOMEM; + goto ext_out; + } + + /* Send our own NAND_CMD_PARAM. */ + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + + /* Use the Change Read Column command to skip the ONFI param pages. */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + sizeof(*p) * p->num_of_param_pages , -1); + + /* Read out the Extended Parameter Page. */ + chip->read_buf(mtd, (uint8_t *)ep, len); + if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) + != le16_to_cpu(ep->crc))) { + pr_debug("fail in the CRC.\n"); + goto ext_out; + } + + /* + * Check the signature. + * Do not strictly follow the ONFI spec, maybe changed in future. + */ + if (strncmp(ep->sig, "EPPS", 4)) { + pr_debug("The signature is invalid.\n"); + goto ext_out; + } + + /* find the ECC section. */ + cursor = (uint8_t *)(ep + 1); + for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { + s = ep->sections + i; + if (s->type == ONFI_SECTION_TYPE_2) + break; + cursor += s->length * 16; + } + if (i == ONFI_EXT_SECTION_MAX) { + pr_debug("We can not find the ECC section.\n"); + goto ext_out; + } + + /* get the info we want. */ + ecc = (struct onfi_ext_ecc_info *)cursor; + + if (ecc->codeword_size) { + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + } + + pr_info("ONFI extended param page detected.\n"); + return 0; + +ext_out: + kfree(ep); + return ret; +} + /* * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. */ @@ -2916,6 +2988,21 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, if (p->ecc_bits != 0xff) { chip->ecc_strength_ds = p->ecc_bits; chip->ecc_step_ds = 512; + } else if (chip->onfi_version >= 21 && + (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { + + /* + * The nand_flash_detect_ext_param_page() uses the + * Change Read Column command which maybe not supported + * by the chip->cmdfunc. So try to update the chip->cmdfunc + * now. We do not replace user supplied command function. + */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + /* The Extended Parameter Page is supported since ONFI 2.1. */ + if (nand_flash_detect_ext_param_page(mtd, chip, p)) + pr_info("Failed to detect the extended param page.\n"); } pr_info("ONFI flash detected\n"); -- cgit v0.10.2 From e2985fc1d684927ce1dd5e5e6d35db8b52302685 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 May 2013 11:17:30 +0800 Subject: mtd: replace the hardcode with the onfi_feature() The current code uses the hardcode to detect the 16-bit bus width. Use the onfi_feature() to replace it. Signed-off-by: Huang Shijie [Brian: small fixup] Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index d8c723c..51d5b81 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2981,9 +2981,11 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); 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) + + if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) *busw = NAND_BUSWIDTH_16; + else + *busw = 0; if (p->ecc_bits != 0xff) { chip->ecc_strength_ds = p->ecc_bits; -- cgit v0.10.2 From 2dc0bdd91bb63f7d5755137177315c49637f6a96 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 May 2013 11:17:31 +0800 Subject: mtd: add ECC info for nand_flash_dev{} Add an instance of an anonymous struct to store the ECC info for full id nand chips. @ecc.strength_ds: ECC correctability from the datasheet. @ecc.step_ds: ECC size required by the @ecc.strength_ds, These two fields are all from the datasheet. Also add the necessary macros to make the code simple and clean. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index bf4c566..ac8e89d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -628,6 +628,11 @@ struct nand_chip { { .name = (nm), {{ .dev_id = (devid) }}, .chipsize = (chipsz), \ .options = (opts) } +#define NAND_ECC_INFO(_strength, _step) \ + { .strength_ds = (_strength), .step_ds = (_step) } +#define NAND_ECC_STRENGTH(type) ((type)->ecc.strength_ds) +#define NAND_ECC_STEP(type) ((type)->ecc.step_ds) + /** * struct nand_flash_dev - NAND Flash Device ID Structure * @name: a human-readable name of the NAND chip @@ -645,6 +650,12 @@ struct nand_chip { * @options: stores various chip bit options * @id_len: The valid length of the @id. * @oobsize: OOB size + * @ecc.strength_ds: The ECC correctability from the datasheet, same as the + * @ecc_strength_ds in nand_chip{}. + * @ecc.step_ds: The ECC step required by the @ecc.strength_ds, same as the + * @ecc_step_ds in nand_chip{}, also from the datasheet. + * For example, the "4bit ECC for each 512Byte" can be set with + * NAND_ECC_INFO(4, 512). */ struct nand_flash_dev { char *name; @@ -661,6 +672,10 @@ struct nand_flash_dev { unsigned int options; uint16_t id_len; uint16_t oobsize; + struct { + uint16_t strength_ds; + uint16_t step_ds; + } ecc; }; /** -- cgit v0.10.2 From 5721934206f5517b159adffd91a9583c509b4e28 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 May 2013 11:17:32 +0800 Subject: mtd: parse out the ECC info for the full-id nand chips Parse out the ECC information for the full-id nand chips. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 51d5b81..8f04fb0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3284,6 +3284,8 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, chip->cellinfo = id_data[2]; chip->chipsize = (uint64_t)type->chipsize << 20; chip->options |= type->options; + chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); + chip->ecc_step_ds = NAND_ECC_STEP(type); *busw = type->options & NAND_BUSWIDTH_16; -- cgit v0.10.2 From d1048aaf6d8b467d226c8d8901017ca48f4e48e9 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 May 2013 11:17:33 +0800 Subject: mtd: add the ecc info for some full-id nand chips Add the ecc info for TC58NVG2S0F, TC58NVG3S0F, TC58NVG5D2 and TC58NVG6D2. From these chips' datasheets, we know that: The TC58NVG2S0F and TC58NVG3S0F require 4bit ECC for per 512byte. The TC58NVG5D2 and TC58NVG6D2 require 40bits ECC for per 1024byte. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 683813a..a87b0a3 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -33,16 +33,16 @@ struct nand_flash_dev nand_flash_ids[] = { */ {"TC58NVG2S0F 4G 3.3V 8-bit", { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, - SZ_4K, SZ_512, SZ_256K, 0, 8, 224}, + SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, {"TC58NVG3S0F 8G 3.3V 8-bit", { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, - SZ_4K, SZ_1K, SZ_256K, 0, 8, 232}, + SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, {"TC58NVG5D2 32G 3.3V 8-bit", { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} }, - SZ_8K, SZ_4K, SZ_1M, 0, 8, 640}, + SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, {"TC58NVG6D2 64G 3.3V 8-bit", { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, - SZ_8K, SZ_8K, SZ_2M, 0, 8, 640}, + SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), -- cgit v0.10.2 From 2febcdf84b75aae627c61f0a5bf531a69299966c Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 May 2013 11:17:34 +0800 Subject: mtd: gpmi: set the BCH's geometry with the ecc info If the nand chip provides us the ECC info, we can use it firstly. The set_geometry_by_ecc_info() will use the ECC info, and calculate the parameters we need. Rename the old code to legacy_set_geometry() which will takes effect when there is no ECC info from the nand chip or we fails in the ECC info case. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 76d2f41..2ab6b3f 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -111,7 +111,132 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) return true; } -int common_nfc_set_geometry(struct gpmi_nand_data *this) +/* + * If we can get the ECC information from the nand chip, we do not + * need to calculate them ourselves. + * + * We may have available oob space in this case. + */ +static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct mtd_info *mtd = &this->mtd; + struct nand_chip *chip = mtd->priv; + struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree; + unsigned int block_mark_bit_offset; + + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return false; + + switch (chip->ecc_step_ds) { + case SZ_512: + geo->gf_len = 13; + break; + case SZ_1K: + geo->gf_len = 14; + break; + default: + dev_err(this->dev, + "unsupported nand chip. ecc bits : %d, ecc size : %d\n", + chip->ecc_strength_ds, chip->ecc_step_ds); + return false; + } + geo->ecc_chunk_size = chip->ecc_step_ds; + geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); + if (!gpmi_check_ecc(this)) + return false; + + /* Keep the C >= O */ + if (geo->ecc_chunk_size < mtd->oobsize) { + dev_err(this->dev, + "unsupported nand chip. ecc size: %d, oob size : %d\n", + chip->ecc_step_ds, mtd->oobsize); + return false; + } + + /* The default value, see comment in the legacy_set_geometry(). */ + geo->metadata_size = 10; + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + /* + * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: + * + * | P | + * |<----------------------------------------------------->| + * | | + * | (Block Mark) | + * | P' | | | | + * |<-------------------------------------------->| D | | O' | + * | |<---->| |<--->| + * V V V V V + * +---+----------+-+----------+-+----------+-+----------+-+-----+ + * | M | data |E| data |E| data |E| data |E| | + * +---+----------+-+----------+-+----------+-+----------+-+-----+ + * ^ ^ + * | O | + * |<------------>| + * | | + * + * P : the page size for BCH module. + * E : The ECC strength. + * G : the length of Galois Field. + * N : The chunk count of per page. + * M : the metasize of per page. + * C : the ecc chunk size, aka the "data" above. + * P': the nand chip's page size. + * O : the nand chip's oob size. + * O': the free oob. + * + * The formula for P is : + * + * E * G * N + * P = ------------ + P' + M + * 8 + * + * The position of block mark moves forward in the ECC-based view + * of page, and the delta is: + * + * E * G * (N - 1) + * D = (---------------- + M) + * 8 + * + * Please see the comment in legacy_set_geometry(). + * With the condition C >= O , we still can get same result. + * So the bit position of the physical block mark within the ECC-based + * view of the page is : + * (P' - D) * 8 + */ + geo->page_size = mtd->writesize + geo->metadata_size + + (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; + + /* The available oob size we have. */ + if (geo->page_size < mtd->writesize + mtd->oobsize) { + of->offset = geo->page_size - mtd->writesize; + of->length = mtd->oobsize - of->offset; + mtd->oobavail = gpmi_hw_ecclayout.oobavail = of->length; + } + + geo->payload_size = mtd->writesize; + + geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); + geo->auxiliary_size = ALIGN(geo->metadata_size, 4) + + ALIGN(geo->ecc_chunk_count, 4); + + if (!this->swap_block_mark) + return true; + + /* For bit swap. */ + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) + + geo->metadata_size * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + return true; +} + +static int legacy_set_geometry(struct gpmi_nand_data *this) { struct bch_geometry *geo = &this->bch_geometry; struct mtd_info *mtd = &this->mtd; @@ -223,6 +348,11 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this) return 0; } +int common_nfc_set_geometry(struct gpmi_nand_data *this) +{ + return set_geometry_by_ecc_info(this) ? 0 : legacy_set_geometry(this); +} + struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) { int chipnr = this->current_chip; -- cgit v0.10.2 From c0f3b8643a6fa2461d70760ec49d21d2b031d611 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sat, 10 Aug 2013 16:34:52 -0300 Subject: mtd: nand: pxa3xx: Introduce 'marvell,armada370-nand' compatible string This driver supports NFCv1 (as found in PXA SoC) and NFCv2 (as found in Armada 370/XP SoC). As both controller has a few differences, a way of distinguishing between the two is needed. This commit introduces a new compatible string 'marvell,armada370-nand' and assigns a compatible data of type enum pxa3xx_nand_variant to allow such distinction. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Cc: devicetree@vger.kernel.org Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 501e380..c4bff66 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -123,6 +123,11 @@ enum { STATE_READY, }; +enum pxa3xx_nand_variant { + PXA3XX_NAND_VARIANT_PXA, + PXA3XX_NAND_VARIANT_ARMADA370, +}; + struct pxa3xx_nand_host { struct nand_chip chip; struct pxa3xx_nand_cmdset *cmdset; @@ -171,6 +176,12 @@ struct pxa3xx_nand_info { struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; unsigned int state; + /* + * This driver supports NFCv1 (as found in PXA SoC) + * and NFCv2 (as found in Armada 370/XP SoC). + */ + enum pxa3xx_nand_variant variant; + int cs; int use_ecc; /* use HW ECC ? */ int use_dma; /* use DMA ? */ @@ -1192,11 +1203,28 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) #ifdef CONFIG_OF static struct of_device_id pxa3xx_nand_dt_ids[] = { - { .compatible = "marvell,pxa3xx-nand" }, + { + .compatible = "marvell,pxa3xx-nand", + .data = (void *)PXA3XX_NAND_VARIANT_PXA, + }, + { + .compatible = "marvell,armada370-nand", + .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370, + }, {} }; MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); +static enum pxa3xx_nand_variant +pxa3xx_nand_get_variant(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); + if (!of_id) + return PXA3XX_NAND_VARIANT_PXA; + return (enum pxa3xx_nand_variant)of_id->data; +} + static int pxa3xx_nand_probe_dt(struct platform_device *pdev) { struct pxa3xx_nand_platform_data *pdata; @@ -1252,6 +1280,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) } info = platform_get_drvdata(pdev); + info->variant = pxa3xx_nand_get_variant(pdev); probe_success = 0; for (cs = 0; cs < pdata->num_cs; cs++) { info->cs = cs; -- cgit v0.10.2 From cd9d11820f4232856d6254a29fbb9c4f51a981c2 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:48 -0300 Subject: mtd: nand: pxa3xx: Handle ECC and DMA enable/disable properly When ECC is not selected, the ECC enable bit must be cleared in the NAND control register. Same applies to DMA. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index c4bff66..1dcda6b 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -314,8 +314,17 @@ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) uint32_t ndcr; ndcr = host->reg_ndcr; - ndcr |= info->use_ecc ? NDCR_ECC_EN : 0; - ndcr |= info->use_dma ? NDCR_DMA_EN : 0; + + if (info->use_ecc) + ndcr |= NDCR_ECC_EN; + else + ndcr &= ~NDCR_ECC_EN; + + if (info->use_dma) + ndcr |= NDCR_DMA_EN; + else + ndcr &= ~NDCR_DMA_EN; + ndcr |= NDCR_ND_RUN; /* clear status bits and run */ -- cgit v0.10.2 From 5bb653e890ca7a5b4ba9042cb60d02f7ddef8c21 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:49 -0300 Subject: mtd: nand: pxa3xx: Allow to set/clear the 'spare enable' field Some commands (such as the ONFI parameter page read) need to clear the 'spare enable' bit. This commit allows to set/clear depending on the prepared command, instead of having it always set. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 1dcda6b..6e28121 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -185,6 +185,7 @@ struct pxa3xx_nand_info { int cs; int use_ecc; /* use HW ECC ? */ int use_dma; /* use DMA ? */ + int use_spare; /* use spare ? */ int is_ready; unsigned int page_size; /* page size of attached chip */ @@ -325,6 +326,11 @@ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) else ndcr &= ~NDCR_DMA_EN; + if (info->use_spare) + ndcr |= NDCR_SPARE_EN; + else + ndcr &= ~NDCR_SPARE_EN; + ndcr |= NDCR_ND_RUN; /* clear status bits and run */ @@ -526,6 +532,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, info->buf_count = 0; info->oob_size = 0; info->use_ecc = 0; + info->use_spare = 1; info->use_dma = (use_dma) ? 1 : 0; info->is_ready = 0; info->retcode = ERR_NONE; -- cgit v0.10.2 From 3a1a344a79de3391f4122975eab1167b641668b9 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:50 -0300 Subject: mtd: nand: pxa3xx: Support command buffer #3 Some newer controllers support a fourth command buffer. This additional command buffer allows to set an arbitrary length count, using the NDCB3.NDLENCNT field, to perform non-standard length operations such as the ONFI parameter page read. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 6e28121..b3fc146 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -197,6 +197,7 @@ struct pxa3xx_nand_info { uint32_t ndcb0; uint32_t ndcb1; uint32_t ndcb2; + uint32_t ndcb3; }; static bool use_dma = 1; @@ -493,9 +494,22 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) nand_writel(info, NDSR, NDSR_WRCMDREQ); status &= ~NDSR_WRCMDREQ; info->state = STATE_CMD_HANDLE; + + /* + * Command buffer registers NDCB{0-2} (and optionally NDCB3) + * must be loaded by writing directly either 12 or 16 + * bytes directly to NDCB0, four bytes at a time. + * + * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored + * but each NDCBx register can be read. + */ nand_writel(info, NDCB0, info->ndcb0); nand_writel(info, NDCB0, info->ndcb1); nand_writel(info, NDCB0, info->ndcb2); + + /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + nand_writel(info, NDCB0, info->ndcb3); } /* clear NDSR to let the controller exit the IRQ */ @@ -554,6 +568,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, default: info->ndcb1 = 0; info->ndcb2 = 0; + info->ndcb3 = 0; break; } -- cgit v0.10.2 From 41a634303f8dbf97c8087773b9d66914d2a9c0fd Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:51 -0300 Subject: mtd: nand: pxa3xx: Use 'length override' in ONFI paramater page read The ONFI command 'parameter page read' needs a non-standard length. Therefore, we enable the 'length override' field in NDCB0 and set a non-zero 'length count' in NDCB3. Additionally, the 'spare enable' bit must be disabled for any command that sets a non-zero 'length count' in NDCB3. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index b3fc146..beef5bc 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -80,6 +80,7 @@ #define NDSR_RDDREQ (0x1 << 1) #define NDSR_WRCMDREQ (0x1) +#define NDCB0_LEN_OVRD (0x1 << 28) #define NDCB0_ST_ROW_EN (0x1 << 26) #define NDCB0_AUTO_RS (0x1 << 25) #define NDCB0_CSEL (0x1 << 24) @@ -562,6 +563,9 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, case NAND_CMD_READOOB: pxa3xx_set_datasize(info); break; + case NAND_CMD_PARAM: + info->use_spare = 0; + break; case NAND_CMD_SEQIN: exec_cmd = 0; break; @@ -637,8 +641,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, info->buf_count = 256; info->ndcb0 |= NDCB0_CMD_TYPE(0) | NDCB0_ADDR_CYC(1) + | NDCB0_LEN_OVRD | cmd; info->ndcb1 = (column & 0xFF); + info->ndcb3 = 256; info->data_size = 256; break; -- cgit v0.10.2 From b7655bcb40883de6fd218395a55d0cc9089e55a1 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:52 -0300 Subject: mtd: nand: pxa3xx: Add a local loop variable This is just a cosmetic change, to make the code more readable. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index beef5bc..9f5d8db 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1320,8 +1320,9 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) info->variant = pxa3xx_nand_get_variant(pdev); probe_success = 0; for (cs = 0; cs < pdata->num_cs; cs++) { + struct mtd_info *mtd = info->host[cs]->mtd; info->cs = cs; - ret = pxa3xx_nand_scan(info->host[cs]->mtd); + ret = pxa3xx_nand_scan(mtd); if (ret) { dev_warn(&pdev->dev, "failed to scan nand at cs %d\n", cs); @@ -1329,7 +1330,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) } ppdata.of_node = pdev->dev.of_node; - ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, + ret = mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts[cs], pdata->nr_parts[cs]); if (!ret) -- cgit v0.10.2 From f455578dd961087a5cf94730d9f6489bb1d355f0 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:53 -0300 Subject: mtd: nand: pxa3xx: Remove hardcoded mtd name There's no advantage in using a hardcoded name for the mtd device. Instead use the provided by the platform_device. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 9f5d8db..8757491 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -244,8 +244,6 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = { /* Define a default flash type setting serve as flash detecting only */ #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0]) -const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL}; - #define NDTR0_tCH(c) (min((c), 7) << 19) #define NDTR0_tCS(c) (min((c), 7) << 16) #define NDTR0_tWH(c) (min((c), 7) << 11) @@ -1091,8 +1089,6 @@ KEEP_CONFIG: host->row_addr_cycles = 3; else host->row_addr_cycles = 2; - - mtd->name = mtd_names[0]; return nand_scan_tail(mtd); } @@ -1321,6 +1317,8 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) probe_success = 0; for (cs = 0; cs < pdata->num_cs; cs++) { struct mtd_info *mtd = info->host[cs]->mtd; + + mtd->name = pdev->name; info->cs = cs; ret = pxa3xx_nand_scan(mtd); if (ret) { -- cgit v0.10.2 From ec82135a011cf657ef02cfa2038331b381e39960 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:54 -0300 Subject: mtd: nand: pxa3xx: Remove unneeded internal cmdset Use the defined macros for NAND command instead of using a constant internal structure. This commit is only a cleanup, there's no functionality modification. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 8757491..ad63e8c 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -131,7 +131,6 @@ enum pxa3xx_nand_variant { struct pxa3xx_nand_host { struct nand_chip chip; - struct pxa3xx_nand_cmdset *cmdset; struct mtd_info *mtd; void *info_data; @@ -205,23 +204,6 @@ static bool use_dma = 1; module_param(use_dma, bool, 0444); MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW"); -/* - * Default NAND flash controller configuration setup by the - * bootloader. This configuration is used only when pdata->keep_config is set - */ -static struct pxa3xx_nand_cmdset default_cmdset = { - .read1 = 0x3000, - .read2 = 0x0050, - .program = 0x1080, - .read_status = 0x0070, - .read_id = 0x0090, - .erase = 0xD060, - .reset = 0x00FF, - .lock = 0x002A, - .unlock = 0x2423, - .lock_status = 0x007A, -}; - static struct pxa3xx_nand_timing timing[] = { { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, @@ -530,7 +512,6 @@ static inline int is_buf_blank(uint8_t *buf, size_t len) static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, uint16_t column, int page_addr) { - uint16_t cmd; int addr_cycle, exec_cmd; struct pxa3xx_nand_host *host; struct mtd_info *mtd; @@ -580,21 +561,17 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, switch (command) { case NAND_CMD_READOOB: case NAND_CMD_READ0: - cmd = host->cmdset->read1; + info->buf_start = column; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | addr_cycle + | NAND_CMD_READ0; + if (command == NAND_CMD_READOOB) - info->buf_start = mtd->writesize + column; - else - info->buf_start = column; + info->buf_start += mtd->writesize; - if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | addr_cycle - | (cmd & NDCB0_CMD1_MASK); - else - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | NDCB0_DBC - | addr_cycle - | cmd; + /* Second command setting for large pages */ + if (host->page_size >= PAGE_CHUNK_SIZE) + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); case NAND_CMD_SEQIN: /* small page addr setting */ @@ -625,62 +602,58 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, break; } - cmd = host->cmdset->program; info->ndcb0 |= NDCB0_CMD_TYPE(0x1) | NDCB0_AUTO_RS | NDCB0_ST_ROW_EN | NDCB0_DBC - | cmd + | (NAND_CMD_PAGEPROG << 8) + | NAND_CMD_SEQIN | addr_cycle; break; case NAND_CMD_PARAM: - cmd = NAND_CMD_PARAM; info->buf_count = 256; info->ndcb0 |= NDCB0_CMD_TYPE(0) | NDCB0_ADDR_CYC(1) | NDCB0_LEN_OVRD - | cmd; + | command; info->ndcb1 = (column & 0xFF); info->ndcb3 = 256; info->data_size = 256; break; case NAND_CMD_READID: - cmd = host->cmdset->read_id; info->buf_count = host->read_id_bytes; info->ndcb0 |= NDCB0_CMD_TYPE(3) | NDCB0_ADDR_CYC(1) - | cmd; + | command; info->ndcb1 = (column & 0xFF); info->data_size = 8; break; case NAND_CMD_STATUS: - cmd = host->cmdset->read_status; info->buf_count = 1; info->ndcb0 |= NDCB0_CMD_TYPE(4) | NDCB0_ADDR_CYC(1) - | cmd; + | command; info->data_size = 8; break; case NAND_CMD_ERASE1: - cmd = host->cmdset->erase; info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3) | NDCB0_DBC - | cmd; + | (NAND_CMD_ERASE2 << 8) + | NAND_CMD_ERASE1; info->ndcb1 = page_addr; info->ndcb2 = 0; break; case NAND_CMD_RESET: - cmd = host->cmdset->reset; info->ndcb0 |= NDCB0_CMD_TYPE(5) - | cmd; + | command; break; @@ -876,7 +849,6 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, } /* calculate flash information */ - host->cmdset = &default_cmdset; host->page_size = f->page_size; host->read_id_bytes = (f->page_size == 2048) ? 4 : 2; @@ -922,7 +894,6 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) } host->reg_ndcr = ndcr & ~NDCR_INT_MASK; - host->cmdset = &default_cmdset; host->ndtr0cs0 = nand_readl(info, NDTR0CS0); host->ndtr1cs0 = nand_readl(info, NDTR1CS0); diff --git a/include/linux/platform_data/mtd-nand-pxa3xx.h b/include/linux/platform_data/mtd-nand-pxa3xx.h index c42f39f..ffb8019 100644 --- a/include/linux/platform_data/mtd-nand-pxa3xx.h +++ b/include/linux/platform_data/mtd-nand-pxa3xx.h @@ -16,19 +16,6 @@ struct pxa3xx_nand_timing { unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ }; -struct pxa3xx_nand_cmdset { - uint16_t read1; - uint16_t read2; - uint16_t program; - uint16_t read_status; - uint16_t read_id; - uint16_t erase; - uint16_t reset; - uint16_t lock; - uint16_t unlock; - uint16_t lock_status; -}; - struct pxa3xx_nand_flash { char *name; uint32_t chip_id; -- cgit v0.10.2 From 48cf7efa6be22e5ab42c16e2700e401f2e6f8bad Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:55 -0300 Subject: mtd: nand: pxa3xx: Move cached registers to info structure This registers are not per-chip (aka host) but controller-wide, so it's better to store them in the global 'info' structure. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index ad63e8c..5e5f530 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -144,10 +144,6 @@ struct pxa3xx_nand_host { unsigned int row_addr_cycles; size_t read_id_bytes; - /* cached register value */ - uint32_t reg_ndcr; - uint32_t ndtr0cs0; - uint32_t ndtr1cs0; }; struct pxa3xx_nand_info { @@ -193,6 +189,11 @@ struct pxa3xx_nand_info { unsigned int oob_size; int retcode; + /* cached register value */ + uint32_t reg_ndcr; + uint32_t ndtr0cs0; + uint32_t ndtr1cs0; + /* generated NDCBx register values */ uint32_t ndcb0; uint32_t ndcb1; @@ -258,8 +259,8 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); - host->ndtr0cs0 = ndtr0; - host->ndtr1cs0 = ndtr1; + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; nand_writel(info, NDTR0CS0, ndtr0); nand_writel(info, NDTR1CS0, ndtr1); } @@ -267,7 +268,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) { struct pxa3xx_nand_host *host = info->host[info->cs]; - int oob_enable = host->reg_ndcr & NDCR_SPARE_EN; + int oob_enable = info->reg_ndcr & NDCR_SPARE_EN; info->data_size = host->page_size; if (!oob_enable) { @@ -293,10 +294,9 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) */ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) { - struct pxa3xx_nand_host *host = info->host[info->cs]; uint32_t ndcr; - ndcr = host->reg_ndcr; + ndcr = info->reg_ndcr; if (info->use_ecc) ndcr |= NDCR_ECC_EN; @@ -683,7 +683,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, * "byte" address into a "word" address appropriate * for indexing a word-oriented device */ - if (host->reg_ndcr & NDCR_DWIDTH_M) + if (info->reg_ndcr & NDCR_DWIDTH_M) column /= 2; /* @@ -693,8 +693,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, */ if (info->cs != host->cs) { info->cs = host->cs; - nand_writel(info, NDTR0CS0, host->ndtr0cs0); - nand_writel(info, NDTR1CS0, host->ndtr1cs0); + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); } info->state = STATE_PREPARED; @@ -870,7 +870,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes); ndcr |= NDCR_SPARE_EN; /* enable spare by default */ - host->reg_ndcr = ndcr; + info->reg_ndcr = ndcr; pxa3xx_nand_set_timing(host, f->timing); return 0; @@ -893,11 +893,9 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) host->read_id_bytes = 2; } - host->reg_ndcr = ndcr & ~NDCR_INT_MASK; - - host->ndtr0cs0 = nand_readl(info, NDTR0CS0); - host->ndtr1cs0 = nand_readl(info, NDTR1CS0); - + info->reg_ndcr = ndcr & ~NDCR_INT_MASK; + info->ndtr0cs0 = nand_readl(info, NDTR0CS0); + info->ndtr1cs0 = nand_readl(info, NDTR1CS0); return 0; } @@ -1044,7 +1042,7 @@ KEEP_CONFIG: chip->ecc.size = host->page_size; chip->ecc.strength = 1; - if (host->reg_ndcr & NDCR_DWIDTH_M) + if (info->reg_ndcr & NDCR_DWIDTH_M) chip->options |= NAND_BUSWIDTH_16; if (nand_scan_ident(mtd, 1, def)) -- cgit v0.10.2 From f4db2e3a3315019c419a7ee8ebaf5d8b361be791 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:56 -0300 Subject: mtd: nand: pxa3xx: Make dma code dependent on dma capable platforms This patch adds a macro ARCH_HAS_DMA to compile-out arch specific dma code, namely pxa_request_dma() and pxa_free_dma(). These symbols are available only in pxa, which makes impossible to build the driver in other platforms than ARCH_PXA. In order to handle non-dma capable platforms, we implement a fallbacks that allocate buffers as if 'use_dma=false', putting the dma related code under the ARCH_HAS_DMA conditional. Please note that the correct way to handle this is to migrate the dma code to use of the mmp_pdma dmaengine driver. However, currently this is not possible because the two dmaengine drivers can't work together. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 5e5f530..ce343bf 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -25,7 +25,14 @@ #include #include +#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP) +#define ARCH_HAS_DMA +#endif + +#ifdef ARCH_HAS_DMA #include +#endif + #include #define CHIP_DELAY_TIMEOUT (2 * HZ/10) @@ -381,6 +388,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info) } } +#ifdef ARCH_HAS_DMA static void start_data_dma(struct pxa3xx_nand_info *info) { struct pxa_dma_desc *desc = info->data_desc; @@ -427,6 +435,10 @@ static void pxa3xx_nand_data_dma_irq(int channel, void *data) enable_int(info, NDCR_INT_MASK); nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); } +#else +static void start_data_dma(struct pxa3xx_nand_info *info) +{} +#endif static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) { @@ -905,6 +917,7 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) */ #define MAX_BUFF_SIZE PAGE_SIZE +#ifdef ARCH_HAS_DMA static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) { struct platform_device *pdev = info->pdev; @@ -950,6 +963,20 @@ static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) kfree(info->data_buff); } } +#else +static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) +{ + info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL); + if (info->data_buff == NULL) + return -ENOMEM; + return 0; +} + +static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) +{ + kfree(info->data_buff); +} +#endif static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info) { @@ -1265,6 +1292,13 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) struct pxa3xx_nand_info *info; int ret, cs, probe_success; +#ifndef ARCH_HAS_DMA + if (use_dma) { + use_dma = 0; + dev_warn(&pdev->dev, + "This platform can't do DMA on this device\n"); + } +#endif ret = pxa3xx_nand_probe_dt(pdev); if (ret) return ret; -- cgit v0.10.2 From 57ff88f0f961348a1dec024d611ec7e281510529 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:57 -0300 Subject: mtd: nand: pxa3xx: Add __maybe_unused keyword to enable_int() Now that we have added ARCH_HAS_DMA conditional the function enable_int() may be unused. Declare it as __maybe_unused, in order to remove the following warning, when the function is not used: drivers/mtd/nand//pxa3xx_nand.c:343:24: warning: 'enable_int' defined but not used [-Wunused-function] Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index ce343bf..d14933c 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -348,7 +348,8 @@ static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) nand_writel(info, NDSR, NDSR_MASK); } -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +static void __maybe_unused +enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) { uint32_t ndcr; -- cgit v0.10.2 From 6b45c1eef6d19d3a6af43471c70235cbe967b491 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:58 -0300 Subject: mtd: nand: pxa3xx: Allow devices with no dma resources When use_dma=0 there's no point in requesting resources for dma, since they won't be used anyway. Therefore we remove that requirement, therefore allowing devices without dma to pass the driver probe. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index d14933c..5db900d 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1141,30 +1141,35 @@ static int alloc_nand_resource(struct platform_device *pdev) if (ret < 0) return ret; - /* - * This is a dirty hack to make this driver work from devicetree - * bindings. It can be removed once we have a prober DMA controller - * framework for DT. - */ - if (pdev->dev.of_node && of_machine_is_compatible("marvell,pxa3xx")) { - info->drcmr_dat = 97; - info->drcmr_cmd = 99; - } else { - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no resource defined for data DMA\n"); - ret = -ENXIO; - goto fail_disable_clk; - } - info->drcmr_dat = r->start; - - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (r == NULL) { - dev_err(&pdev->dev, "no resource defined for command DMA\n"); - ret = -ENXIO; - goto fail_disable_clk; + if (use_dma) { + /* + * This is a dirty hack to make this driver work from + * devicetree bindings. It can be removed once we have + * a prober DMA controller framework for DT. + */ + if (pdev->dev.of_node && + of_machine_is_compatible("marvell,pxa3xx")) { + info->drcmr_dat = 97; + info->drcmr_cmd = 99; + } else { + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r == NULL) { + dev_err(&pdev->dev, + "no resource defined for data DMA\n"); + ret = -ENXIO; + goto fail_disable_clk; + } + info->drcmr_dat = r->start; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r == NULL) { + dev_err(&pdev->dev, + "no resource defined for cmd DMA\n"); + ret = -ENXIO; + goto fail_disable_clk; + } + info->drcmr_cmd = r->start; } - info->drcmr_cmd = r->start; } irq = platform_get_irq(pdev, 0); -- cgit v0.10.2 From 0d58f6ee0106256c47db9640141469d1ed80819a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 12 Aug 2013 14:14:59 -0300 Subject: mtd: nand: Allow to build pxa3xx_nand on Orion platforms The Armada 370 and Armada XP SoC families, selected by PLAT_ORION, have a Nand Flash Controller (NFC) IP very similar to the one present in PXA platforms. Therefore, we want to build this driver on PLAT_ORION. Signed-off-by: Ezequiel Garcia Tested-by: Daniel Mack Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5ef8f5e..86c135b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -354,7 +354,7 @@ config MTD_NAND_ATMEL config MTD_NAND_PXA3xx tristate "Support for NAND flash devices on PXA3xx" - depends on PXA3xx || ARCH_MMP + depends on PXA3xx || ARCH_MMP || PLAT_ORION help This enables the driver for the NAND flash device found on PXA3xx processors -- cgit v0.10.2 From abc173ad84e50645f9136ea56afa9f86c98bf349 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 15 Aug 2013 22:55:08 +0900 Subject: mtd: tests: incorporate error message for mtdtest_read() All callers of mtdtest_read() print the same error message on failure. This incorporates the error message to mtdtest_read() and removes them from the callers. Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c index bda8c4d..4c2e3f0 100644 --- a/drivers/mtd/tests/mtd_test.c +++ b/drivers/mtd/tests/mtd_test.c @@ -92,6 +92,8 @@ int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf) err = 0; if (!err && read != size) err = -EIO; + if (err) + pr_err("error: read failed at %#llx\n", addr); return err; } diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c index 8318976..9e8329c 100644 --- a/drivers/mtd/tests/pagetest.c +++ b/drivers/mtd/tests/pagetest.c @@ -84,24 +84,15 @@ static int verify_eraseblock(int ebnum) for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { /* Do a read to set the internal dataRAMs to different data */ err = mtdtest_read(mtd, addr0, bufsize, twopages); - if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); + if (err) return err; - } err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages); - if (err) { - pr_err("error: read failed at %#llx\n", - (long long)(addrn - bufsize)); + if (err) return err; - } memset(twopages, 0, bufsize); err = mtdtest_read(mtd, addr, bufsize, twopages); - if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + if (err) break; - } if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) { pr_err("error: verify failed at %#llx\n", (long long)addr); @@ -114,24 +105,15 @@ static int verify_eraseblock(int ebnum) /* Do a read to set the internal dataRAMs to different data */ err = mtdtest_read(mtd, addr0, bufsize, twopages); - if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); + if (err) return err; - } err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages); - if (err) { - pr_err("error: read failed at %#llx\n", - (long long)(addrn - bufsize)); + if (err) return err; - } memset(twopages, 0, bufsize); err = mtdtest_read(mtd, addr, bufsize, twopages); - if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + if (err) return err; - } memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize); if (memcmp(twopages, boundary, bufsize)) { @@ -171,8 +153,6 @@ static int crosstest(void) addr = addrn - pgsize - pgsize; err = mtdtest_read(mtd, addr, pgsize, pp1); if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr); kfree(pp1); return err; } @@ -181,8 +161,6 @@ static int crosstest(void) addr = addrn - pgsize - pgsize - pgsize; err = mtdtest_read(mtd, addr, pgsize, pp1); if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr); kfree(pp1); return err; } @@ -192,8 +170,6 @@ static int crosstest(void) pr_info("reading page at %#llx\n", (long long)addr); err = mtdtest_read(mtd, addr, pgsize, pp2); if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr); kfree(pp1); return err; } @@ -203,8 +179,6 @@ static int crosstest(void) pr_info("reading page at %#llx\n", (long long)addr); err = mtdtest_read(mtd, addr, pgsize, pp3); if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr); kfree(pp1); return err; } @@ -214,8 +188,6 @@ static int crosstest(void) pr_info("reading page at %#llx\n", (long long)addr); err = mtdtest_read(mtd, addr, pgsize, pp4); if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr); kfree(pp1); return err; } @@ -269,11 +241,8 @@ static int erasecrosstest(void) pr_info("reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); err = mtdtest_read(mtd, addr0, pgsize, readbuf); - if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); + if (err) return err; - } pr_info("verifying 1st page of block %d\n", ebnum); if (memcmp(writebuf, readbuf, pgsize)) { @@ -305,11 +274,8 @@ static int erasecrosstest(void) pr_info("reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); err = mtdtest_read(mtd, addr0, pgsize, readbuf); - if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); + if (err) return err; - } pr_info("verifying 1st page of block %d\n", ebnum); if (memcmp(writebuf, readbuf, pgsize)) { @@ -358,11 +324,8 @@ static int erasetest(void) pr_info("reading 1st page of block %d\n", ebnum); err = mtdtest_read(mtd, addr0, pgsize, twopages); - if (err) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); + if (err) return err; - } pr_info("verifying 1st page of block %d is all 0xff\n", ebnum); diff --git a/drivers/mtd/tests/readtest.c b/drivers/mtd/tests/readtest.c index ffd50d6..626e66d 100644 --- a/drivers/mtd/tests/readtest.c +++ b/drivers/mtd/tests/readtest.c @@ -55,8 +55,6 @@ static int read_eraseblock_by_page(int ebnum) memset(buf, 0 , pgsize); ret = mtdtest_read(mtd, addr, pgsize, buf); if (ret) { - pr_err("error: read failed at %#llx\n", - (long long)addr); if (!err) err = ret; } diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c index 457c45c..d1a3853 100644 --- a/drivers/mtd/tests/speedtest.c +++ b/drivers/mtd/tests/speedtest.c @@ -140,14 +140,9 @@ static int write_eraseblock_by_2pages(int ebnum) static int read_eraseblock(int ebnum) { - int err; loff_t addr = ebnum * mtd->erasesize; - err = mtdtest_read(mtd, addr, mtd->erasesize, iobuf); - if (err) - pr_err("error: read failed at %#llx\n", addr); - - return err; + return mtdtest_read(mtd, addr, mtd->erasesize, iobuf); } static int read_eraseblock_by_page(int ebnum) @@ -158,11 +153,8 @@ static int read_eraseblock_by_page(int ebnum) for (i = 0; i < pgcnt; i++) { err = mtdtest_read(mtd, addr, pgsize, buf); - if (err) { - pr_err("error: read failed at %#llx\n", - addr); + if (err) break; - } addr += pgsize; buf += pgsize; } @@ -179,21 +171,13 @@ static int read_eraseblock_by_2pages(int ebnum) for (i = 0; i < n; i++) { err = mtdtest_read(mtd, addr, sz, buf); - if (err) { - pr_err("error: read failed at %#llx\n", - addr); + if (err) return err; - } addr += sz; buf += sz; } - if (pgcnt % 2) { + if (pgcnt % 2) err = mtdtest_read(mtd, addr, pgsize, buf); - if (err) { - pr_err("error: read failed at %#llx\n", - addr); - } - } return err; } diff --git a/drivers/mtd/tests/stresstest.c b/drivers/mtd/tests/stresstest.c index 1695248..6c9e23f 100644 --- a/drivers/mtd/tests/stresstest.c +++ b/drivers/mtd/tests/stresstest.c @@ -87,7 +87,7 @@ static int do_read(void) { int eb = rand_eb(); int offs = rand_offs(); - int len = rand_len(offs), err; + int len = rand_len(offs); loff_t addr; if (bbt[eb + 1]) { @@ -97,13 +97,7 @@ static int do_read(void) len = mtd->erasesize - offs; } addr = eb * mtd->erasesize + offs; - err = mtdtest_read(mtd, addr, len, readbuf); - if (unlikely(err)) { - pr_err("error: read failed at 0x%llx\n", - (long long)addr); - return err; - } - return 0; + return mtdtest_read(mtd, addr, len, readbuf); } static int do_write(void) -- cgit v0.10.2 From 8a9f4aa3ac7560efcb242b3d1a31b1d804106f12 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 15 Aug 2013 22:55:09 +0900 Subject: mtd: tests: incorporate error message for mtdtest_write() All callers of mtdtest_write() print the same error message on failure. This incorporates the error message to mtdtest_write() and removes them from the callers. Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c index 4c2e3f0..c818a63 100644 --- a/drivers/mtd/tests/mtd_test.c +++ b/drivers/mtd/tests/mtd_test.c @@ -107,6 +107,8 @@ int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size, err = mtd_write(mtd, addr, size, &written, buf); if (!err && written != size) err = -EIO; + if (err) + pr_err("error: write failed at %#llx\n", addr); return err; } diff --git a/drivers/mtd/tests/nandbiterrs.c b/drivers/mtd/tests/nandbiterrs.c index 5a8c858..3cd3aab 100644 --- a/drivers/mtd/tests/nandbiterrs.c +++ b/drivers/mtd/tests/nandbiterrs.c @@ -102,16 +102,10 @@ static uint8_t hash(unsigned offset) /* Writes wbuffer to page */ static int write_page(int log) { - int err; - if (log) pr_info("write_page\n"); - err = mtdtest_write(mtd, offset, mtd->writesize, wbuffer); - if (err) - pr_err("error: write failed at %#llx\n", (long long)offset); - - return err; + return mtdtest_write(mtd, offset, mtd->writesize, wbuffer); } /* Re-writes the data area while leaving the OOB alone. */ diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c index 9e8329c..44b96e9 100644 --- a/drivers/mtd/tests/pagetest.c +++ b/drivers/mtd/tests/pagetest.c @@ -52,17 +52,11 @@ static struct rnd_state rnd_state; static int write_eraseblock(int ebnum) { - int err; loff_t addr = ebnum * mtd->erasesize; prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); cond_resched(); - err = mtdtest_write(mtd, addr, mtd->erasesize, writebuf); - if (err) - pr_err("error: write failed at %#llx\n", - (long long)addr); - - return err; + return mtdtest_write(mtd, addr, mtd->erasesize, writebuf); } static int verify_eraseblock(int ebnum) @@ -232,11 +226,8 @@ static int erasecrosstest(void) prandom_bytes_state(&rnd_state, writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); err = mtdtest_write(mtd, addr0, pgsize, writebuf); - if (err) { - pr_info("error: write failed at %#llx\n", - (long long)addr0); + if (err) return err; - } pr_info("reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); @@ -260,11 +251,8 @@ static int erasecrosstest(void) prandom_bytes_state(&rnd_state, writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); err = mtdtest_write(mtd, addr0, pgsize, writebuf); - if (err) { - pr_err("error: write failed at %#llx\n", - (long long)addr0); + if (err) return err; - } pr_info("erasing block %d\n", ebnum2); err = mtdtest_erase_eraseblock(mtd, ebnum2); @@ -311,11 +299,8 @@ static int erasetest(void) pr_info("writing 1st page of block %d\n", ebnum); prandom_bytes_state(&rnd_state, writebuf, pgsize); err = mtdtest_write(mtd, addr0, pgsize, writebuf); - if (err) { - pr_err("error: write failed at %#llx\n", - (long long)addr0); + if (err) return err; - } pr_info("erasing block %d\n", ebnum); err = mtdtest_erase_eraseblock(mtd, ebnum); diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c index d1a3853..87ff6a2 100644 --- a/drivers/mtd/tests/speedtest.c +++ b/drivers/mtd/tests/speedtest.c @@ -80,14 +80,9 @@ static int multiblock_erase(int ebnum, int blocks) static int write_eraseblock(int ebnum) { - int err; loff_t addr = ebnum * mtd->erasesize; - err = mtdtest_write(mtd, addr, mtd->erasesize, iobuf); - if (err) - pr_err("error: write failed at %#llx\n", addr); - - return err; + return mtdtest_write(mtd, addr, mtd->erasesize, iobuf); } static int write_eraseblock_by_page(int ebnum) @@ -98,11 +93,8 @@ static int write_eraseblock_by_page(int ebnum) for (i = 0; i < pgcnt; i++) { err = mtdtest_write(mtd, addr, pgsize, buf); - if (err) { - pr_err("error: write failed at %#llx\n", - addr); + if (err) break; - } addr += pgsize; buf += pgsize; } @@ -119,21 +111,13 @@ static int write_eraseblock_by_2pages(int ebnum) for (i = 0; i < n; i++) { err = mtdtest_write(mtd, addr, sz, buf); - if (err) { - pr_err("error: write failed at %#llx\n", - addr); + if (err) return err; - } addr += sz; buf += sz; } - if (pgcnt % 2) { + if (pgcnt % 2) err = mtdtest_write(mtd, addr, pgsize, buf); - if (err) { - pr_err("error: write failed at %#llx\n", - addr); - } - } return err; } diff --git a/drivers/mtd/tests/stresstest.c b/drivers/mtd/tests/stresstest.c index 6c9e23f..c9d42cc 100644 --- a/drivers/mtd/tests/stresstest.c +++ b/drivers/mtd/tests/stresstest.c @@ -126,11 +126,8 @@ static int do_write(void) } addr = eb * mtd->erasesize + offs; err = mtdtest_write(mtd, addr, len, writebuf); - if (unlikely(err)) { - pr_err("error: write failed at 0x%llx\n", - (long long)addr); + if (unlikely(err)) return err; - } offs += len; while (offs > mtd->erasesize) { offsets[eb++] = mtd->erasesize; -- cgit v0.10.2 From 39de86ef0d333b210d27edad828d4f7cf68feff3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Aug 2013 10:56:29 +0300 Subject: mtd: create_freezable_workqueue() doesn't return an ERR_PTR The create_freezable_workqueue() returns a NULL on error, it doesn't return an ERR_PTR. Signed-off-by: Dan Carpenter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index f9d5615..3441e73 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -1274,10 +1274,10 @@ static struct mtd_blktrans_ops sm_ftl_ops = { static __init int sm_module_init(void) { int error = 0; - cache_flush_workqueue = create_freezable_workqueue("smflush"); - if (IS_ERR(cache_flush_workqueue)) - return PTR_ERR(cache_flush_workqueue); + cache_flush_workqueue = create_freezable_workqueue("smflush"); + if (!cache_flush_workqueue) + return -ENOMEM; error = register_mtd_blktrans(&sm_ftl_ops); if (error) -- cgit v0.10.2 From 7a96541a8e9fe720bec3900679ca514fefd2d516 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 14 Aug 2013 11:11:08 +0200 Subject: mtd: simplify use of devm_ioremap_resource Remove unneeded error handling on the result of a call to platform_get_resource when the value is passed to devm_ioremap_resource. Move the call to platform_get_resource adjacent to the call to devm_ioremap_resource to make the connection between them more clear. A simplified version of the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression pdev,res,n,e,e1; expression ret != 0; identifier l; @@ - res = platform_get_resource(pdev, IORESOURCE_MEM, n); ... when != res - if (res == NULL) { ... \(goto l;\|return ret;\) } ... when != res + res = platform_get_resource(pdev, IORESOURCE_MEM, n); e = devm_ioremap_resource(e1, res); // Signed-off-by: Julia Lawall Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index 4f683d2..d1dd6a3 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -368,11 +368,6 @@ static int elm_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no memory resource defined\n"); - return -ENODEV; - } - info->elm_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(info->elm_base)) return PTR_ERR(info->elm_base); diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 440b570..235714a 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -281,12 +281,10 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) unsigned long gbusclk = plat->gbus_clock; struct resource *res; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; + res = platform_get_resource(dev, IORESOURCE_MEM, 0); drvdata->base = devm_ioremap_resource(&dev->dev, res); if (IS_ERR(drvdata->base)) return PTR_ERR(drvdata->base); -- cgit v0.10.2 From daae74cad4bcd4566fcb82477e33c42bcdaa86e8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 9 Aug 2013 12:49:05 +0300 Subject: mtd: nand: silence some shift wrap warnings There are static checkers which complain when we declare variables as 64 bit bitfields but only use the lower 32 bits because of shift wrapping. In this case "len" is declared as u64 as opposed to unsigned long or something which might be 32 bits. Signed-off-by: Dan Carpenter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8f04fb0..761ef2f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -108,13 +108,13 @@ static int check_offs_len(struct mtd_info *mtd, int ret = 0; /* Start address must align on block boundary */ - if (ofs & ((1 << chip->phys_erase_shift) - 1)) { + if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { pr_debug("%s: unaligned address\n", __func__); ret = -EINVAL; } /* Length must align on block boundary */ - if (len & ((1 << chip->phys_erase_shift) - 1)) { + if (len & ((1ULL << chip->phys_erase_shift) - 1)) { pr_debug("%s: length not block aligned\n", __func__); ret = -EINVAL; } @@ -394,7 +394,7 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) memset(&einfo, 0, sizeof(einfo)); einfo.mtd = mtd; einfo.addr = ofs; - einfo.len = 1 << chip->phys_erase_shift; + einfo.len = 1ULL << chip->phys_erase_shift; nand_erase_nand(mtd, &einfo, 0); /* Write bad block marker to OOB */ @@ -2630,7 +2630,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, } /* Increment page address and decrement length */ - len -= (1 << chip->phys_erase_shift); + len -= (1ULL << chip->phys_erase_shift); page += pages_per_block; /* Check, if we cross a chip boundary */ -- cgit v0.10.2 From c41a0582d59e3bc41978ac4a23e457d86ac7365f Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Aug 2013 10:10:04 +0800 Subject: mtd: add a new ecc_step_size field to mtd_info{} In order to implement the NAND boot for some Freescale's chips, such as imx23/imx28/imx50/imx6, we use a tool (called kobs-ng) to burn the uboot and some metadata to nand chip. And the ROM code will use the metadata to configrate the BCH, and to find the uboot. The ECC information(ecc step size, ecc strength) which is used to configrure the BCH is part of the metadata. The kobs-ng can get the ecc strength from the sys node /sys/*/ecc_strength now. But it can not get the ecc step size. This patch adds a new field to store the ecc step size in mtd_info{}, and it makes preparation for the next patches. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index a5cf4e8..f9bfe52 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -173,6 +173,9 @@ struct mtd_info { /* ECC layout structure pointer - read only! */ struct nand_ecclayout *ecclayout; + /* the ecc step size. */ + unsigned int ecc_step_size; + /* max number of correctible bit errors per ecc step */ unsigned int ecc_strength; -- cgit v0.10.2 From bf977e3f158dc949fd6e1e4195f80d42232cb5fa Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Aug 2013 10:10:05 +0800 Subject: mtd: add a new sys node to show the ecc step size Add a new sys node to show the ecc step size. The application then can uses this node to get the ecc step size. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 048c823..5e14d54 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -285,6 +285,16 @@ static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR, mtd_bitflip_threshold_show, mtd_bitflip_threshold_store); +static ssize_t mtd_ecc_step_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_step_size); + +} +static DEVICE_ATTR(ecc_step_size, S_IRUGO, mtd_ecc_step_size_show, NULL); + static struct attribute *mtd_attrs[] = { &dev_attr_type.attr, &dev_attr_flags.attr, @@ -296,6 +306,7 @@ static struct attribute *mtd_attrs[] = { &dev_attr_numeraseregions.attr, &dev_attr_name.attr, &dev_attr_ecc_strength.attr, + &dev_attr_ecc_step_size.attr, &dev_attr_bitflip_threshold.attr, NULL, }; -- cgit v0.10.2 From bdf69c47ac67480122b8842acf0ea75c0d18a5e9 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Aug 2013 10:10:06 +0800 Subject: mtd: set the ecc step size for master/slave mtd_info Set the ecc step size for master/slave mtd_info{}. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 3014933..6e732c3 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -516,6 +516,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, } slave->mtd.ecclayout = master->ecclayout; + slave->mtd.ecc_step_size = master->ecc_step_size; slave->mtd.ecc_strength = master->ecc_strength; slave->mtd.bitflip_threshold = master->bitflip_threshold; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 761ef2f..49a9780 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3788,6 +3788,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* propagate ecc info to mtd_info */ mtd->ecclayout = chip->ecc.layout; mtd->ecc_strength = chip->ecc.strength; + mtd->ecc_step_size = chip->ecc.size; /* * Initialize bitflip_threshold to its default prior scan_bbt() call. * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be -- cgit v0.10.2 From 4204cccde5e754745bbf30b077060c2033d29efa Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Aug 2013 10:10:07 +0800 Subject: mtd: set ONFI nand's default hooks in nand_set_defaults() We may do some ONFI get/set features operations before we call the nand_scan_tail(). So move the default ONFI nand hooks into nand_set_defaults(). Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 49a9780..80394cc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2794,6 +2794,12 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) if (!chip->select_chip) chip->select_chip = nand_select_chip; + /* set for ONFI nand */ + if (!chip->onfi_set_features) + chip->onfi_set_features = nand_onfi_set_features; + if (!chip->onfi_get_features) + chip->onfi_get_features = nand_onfi_get_features; + /* If called twice, pointers that depend on busw may need to be reset */ if (!chip->read_byte || chip->read_byte == nand_read_byte) chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; @@ -3560,12 +3566,6 @@ int nand_scan_tail(struct mtd_info *mtd) if (!chip->write_page) chip->write_page = nand_write_page; - /* set for ONFI nand */ - if (!chip->onfi_set_features) - chip->onfi_set_features = nand_onfi_set_features; - if (!chip->onfi_get_features) - chip->onfi_get_features = nand_onfi_get_features; - /* * Check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC -- cgit v0.10.2 From f720e7ce510bf79f029be45ce200ccfce5551280 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Aug 2013 10:10:08 +0800 Subject: mtd: gpmi: remove the nand_scan() In order to make the nand_scan() work, the current code uses the hack code to init the @nand_chip->ecc.size and the @nand_chip->ecc.strength. and re-init some the ECC info in the gpmi_pre_bbt_scan(). This code is really a little ugly. The patch does following changes: (1) Use the nand_scan_ident()/nand_scan_tail() to replace the nand_scan(). (2) Init all the necessary values in the gpmi_init_last() before we call the nand_scan_tail(). (3) remove the code setting the ECC info, let the mtd layer to do the real job. (4) remove the gpmi_scan_bbt(). we do not need this function any more. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 2ab6b3f..59ab069 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -214,7 +214,6 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) if (geo->page_size < mtd->writesize + mtd->oobsize) { of->offset = geo->page_size - mtd->writesize; of->length = mtd->oobsize - of->offset; - mtd->oobavail = gpmi_hw_ecclayout.oobavail = of->length; } geo->payload_size = mtd->writesize; @@ -1582,19 +1581,22 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this) if (ret) return ret; - /* Adjust the ECC strength according to the chip. */ - this->nand.ecc.strength = this->bch_geometry.ecc_strength; - this->mtd.ecc_strength = this->bch_geometry.ecc_strength; - this->mtd.bitflip_threshold = this->bch_geometry.ecc_strength; - /* NAND boot init, depends on the gpmi_set_geometry(). */ return nand_boot_init(this); } -static int gpmi_scan_bbt(struct mtd_info *mtd) +static void gpmi_nfc_exit(struct gpmi_nand_data *this) +{ + nand_release(&this->mtd); + gpmi_free_dma_buffer(this); +} + +static int gpmi_init_last(struct gpmi_nand_data *this) { + struct mtd_info *mtd = &this->mtd; struct nand_chip *chip = mtd->priv; - struct gpmi_nand_data *this = chip->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct bch_geometry *bch_geo = &this->bch_geometry; int ret; /* Prepare for the BBT scan. */ @@ -1602,6 +1604,16 @@ static int gpmi_scan_bbt(struct mtd_info *mtd) if (ret) return ret; + /* Init the nand_ecc_ctrl{} */ + ecc->read_page = gpmi_ecc_read_page; + ecc->write_page = gpmi_ecc_write_page; + ecc->read_oob = gpmi_ecc_read_oob; + ecc->write_oob = gpmi_ecc_write_oob; + ecc->mode = NAND_ECC_HW; + ecc->size = bch_geo->ecc_chunk_size; + ecc->strength = bch_geo->ecc_strength; + ecc->layout = &gpmi_hw_ecclayout; + /* * Can we enable the extra features? such as EDO or Sync mode. * @@ -1610,14 +1622,7 @@ static int gpmi_scan_bbt(struct mtd_info *mtd) */ gpmi_extra_init(this); - /* use the default BBT implementation */ - return nand_default_bbt(mtd); -} - -static void gpmi_nfc_exit(struct gpmi_nand_data *this) -{ - nand_release(&this->mtd); - gpmi_free_dma_buffer(this); + return 0; } static int gpmi_nfc_init(struct gpmi_nand_data *this) @@ -1643,33 +1648,33 @@ static int gpmi_nfc_init(struct gpmi_nand_data *this) chip->read_byte = gpmi_read_byte; chip->read_buf = gpmi_read_buf; chip->write_buf = gpmi_write_buf; - chip->ecc.read_page = gpmi_ecc_read_page; - chip->ecc.write_page = gpmi_ecc_write_page; - chip->ecc.read_oob = gpmi_ecc_read_oob; - chip->ecc.write_oob = gpmi_ecc_write_oob; - chip->scan_bbt = gpmi_scan_bbt; chip->badblock_pattern = &gpmi_bbt_descr; chip->block_markbad = gpmi_block_markbad; chip->options |= NAND_NO_SUBPAGE_WRITE; - chip->ecc.mode = NAND_ECC_HW; - chip->ecc.size = 1; - chip->ecc.strength = 8; - chip->ecc.layout = &gpmi_hw_ecclayout; if (of_get_nand_on_flash_bbt(this->dev->of_node)) chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; - /* Allocate a temporary DMA buffer for reading ID in the nand_scan() */ + /* + * Allocate a temporary DMA buffer for reading ID in the + * nand_scan_ident(). + */ this->bch_geometry.payload_size = 1024; this->bch_geometry.auxiliary_size = 128; ret = gpmi_alloc_dma_buffer(this); if (ret) goto err_out; - ret = nand_scan(mtd, 1); - if (ret) { - pr_err("Chip scan failed\n"); + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + goto err_out; + + ret = gpmi_init_last(this); + if (ret) + goto err_out; + + ret = nand_scan_tail(mtd); + if (ret) goto err_out; - } ppdata.of_node = this->pdev->dev.of_node; ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); -- cgit v0.10.2 From ed202940335e3f9930509025e3d75721e564baca Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Aug 2013 10:10:09 +0800 Subject: mtd: update the ABI document about the ecc step size We add a new sys node for ecc step size. So update the ABI document about it. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy [Brian: edited description, modified 'ecc_strength'] Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd index 3105644..bfd119a 100644 --- a/Documentation/ABI/testing/sysfs-class-mtd +++ b/Documentation/ABI/testing/sysfs-class-mtd @@ -128,9 +128,8 @@ KernelVersion: 3.4 Contact: linux-mtd@lists.infradead.org Description: Maximum number of bit errors that the device is capable of - correcting within each region covering an ecc step. This will - always be a non-negative integer. Note that some devices will - have multiple ecc steps within each writesize region. + correcting within each region covering an ECC step (see + ecc_step_size). This will always be a non-negative integer. In the case of devices lacking any ECC capability, it is 0. @@ -173,3 +172,15 @@ Description: This is generally applicable only to NAND flash devices with ECC capability. It is ignored on devices lacking ECC capability; i.e., devices for which ecc_strength is zero. + +What: /sys/class/mtd/mtdX/ecc_step_size +Date: May 2013 +KernelVersion: 3.10 +Contact: linux-mtd@lists.infradead.org +Description: + The size of a single region covered by ECC, known as the ECC + step. Devices may have several equally sized ECC steps within + each writesize region. + + It will always be a non-negative integer. In the case of + devices lacking any ECC capability, it is 0. -- cgit v0.10.2 From d6a95080a2fb9fb38b4f279e97e508f6151c1000 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 8 Aug 2013 17:16:36 -0700 Subject: mtd: nand: fixup kerneldoc, rename parameter First, the function argument is 'offset' not 'column'. Second, the 'data_buf' name is inconsistent with the rest of this file. Just use 'buf'. Signed-off-by: Brian Norris Cc: Gupta, Pekon Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 80394cc..7ed4841 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1980,13 +1980,14 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write * @mtd: mtd info structure * @chip: nand chip info structure - * @column: column address of subpage within the page + * @offset: column address of subpage within the page * @data_len: data length + * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB */ static int nand_write_subpage_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, - uint32_t data_len, const uint8_t *data_buf, + uint32_t data_len, const uint8_t *buf, int oob_required) { uint8_t *oob_buf = chip->oob_poi; @@ -2005,20 +2006,20 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, chip->ecc.hwctl(mtd, NAND_ECC_WRITE); /* write data (untouched subpages already masked by 0xFF) */ - chip->write_buf(mtd, data_buf, ecc_size); + chip->write_buf(mtd, buf, ecc_size); /* mask ECC of un-touched subpages by padding 0xFF */ if ((step < start_step) || (step > end_step)) memset(ecc_calc, 0xff, ecc_bytes); else - chip->ecc.calculate(mtd, data_buf, ecc_calc); + chip->ecc.calculate(mtd, buf, ecc_calc); /* mask OOB of un-touched subpages by padding 0xFF */ /* if oob_required, preserve OOB metadata of written subpage */ if (!oob_required || (step < start_step) || (step > end_step)) memset(oob_buf, 0xff, oob_bytes); - data_buf += ecc_size; + buf += ecc_size; ecc_calc += ecc_bytes; oob_buf += oob_bytes; } -- cgit v0.10.2 From eedeac3ce94e2a3d4ff28974221b9f309358d8de Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Sat, 17 Aug 2013 12:16:29 -0700 Subject: mtd: m25p80: Micron SPI uses Macronix-style 4-byte addressing For SPI NOR flash that are larger than 128Mbit (16MiB), we need 4 bytes of address space to reach the entire flash; however, the original SPI flash protocol used only 3 bytes for the address. So far, the practice for handling this has been either to use new command opcodes that are defined to use 4 bytes for their address, or to use special mode-switching command to configure all traditionally-3-byte-address commands to take 4 bytes instead. Macronix and Spansion developed two incompatible methods for entering/exiting "4-byte address mode." Micron flash uses the Macronix method (OPCODE_{EN4B,EX4B}), not the Spansion method. This patch solves addressing issues on Micron n25q256a and provides the ability to support other future Micron SPI flash >16MiB. Quoting a Micron representative: "Majority of our NOR that needs 4-byte addressing (256Mb or 32MB and higher) enter and exit 4byte through B7h and E9h commands. The N25Q256A7xxx and N25Q512A7xxx parts do not support 4-byte addressing mode via B7h or E9h command." They further clarified that those that don't support the enter/exit opcodes (B7h/E9h) are manufactured specifically to come up by default in 4-byte mode. We don't need to treat those parts any diffently, as they will discard the EN4B opcode as a no-op. Signed-off-by: Brian Norris Acked-by: Marek Vasut Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index b5190c4..a759c1f 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -169,6 +169,7 @@ static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) { switch (JEDEC_MFR(jedec_id)) { case CFI_MFR_MACRONIX: + case CFI_MFR_ST: /* Micron, actually */ case 0xEF /* winbond */: flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B; return spi_write(flash->spi, flash->command, 1); -- cgit v0.10.2 From 2fa831f9db1f1a13960f4ea0fd0454402717b453 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Mon, 19 Aug 2013 18:05:44 +0800 Subject: mtd: atmel_nand: pmecc: fix failure to correct bit error in 1024-bytes sector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PMECC use BCH algorithm to correct error. In BCH algorithm, the primitive polynomial value is GF(2^13) for 512-bytes sector size. And it is GF(2^14) for 1024-bytes sector size. This patch will choose correct degree of the remainders (13 or 14) for different sector size. Tested in AT91SAM9X5-EK with MLC nand flash. More detail can be found in §5.4.1 of: AT91SAM ARM-based Embedded MPU Application Note Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 0e365da..8b2eb3e 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1204,7 +1204,8 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, /* set ECC page size and oob layout */ switch (mtd->writesize) { case 2048: - host->pmecc_degree = PMECC_GF_DIMENSION_13; + host->pmecc_degree = (sector_size == 512) ? + PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14; host->pmecc_cw_len = (1 << host->pmecc_degree) - 1; host->pmecc_sector_number = mtd->writesize / sector_size; host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes( -- cgit v0.10.2 From 44a417fa0fbf41a48f09cf90d06550764b530c0e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 19 Aug 2013 10:51:53 +0200 Subject: mtd: fsmc_nand: simplify platform_get_resource_byname/devm_ioremap_resource Remove unneeded error handling on the result of a call to platform_get_resource_byname when the value is passed to devm_ioremap_resource. A simplified version of the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression pdev,res,e,e1; expression ret != 0; identifier l; @@ res = platform_get_resource_byname(...); - if (res == NULL) { ... \(goto l;\|return ret;\) } e = devm_ioremap_resource(e1, res); // Signed-off-by: Julia Lawall Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 2a3b1b9..3dc1a75 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -958,9 +958,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - if (!res) - return -EINVAL; - host->data_va = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->data_va)) return PTR_ERR(host->data_va); @@ -968,25 +965,16 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->data_pa = (dma_addr_t)res->start; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); - if (!res) - return -EINVAL; - host->addr_va = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->addr_va)) return PTR_ERR(host->addr_va); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); - if (!res) - return -EINVAL; - host->cmd_va = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->cmd_va)) return PTR_ERR(host->cmd_va); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); - if (!res) - return -EINVAL; - host->regs_va = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->regs_va)) return PTR_ERR(host->regs_va); -- cgit v0.10.2 From da887588af616236e55976039debd0a877a4bf92 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 18 Aug 2013 13:03:59 +0200 Subject: mtd: MTD_NAND_DENALI should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `denali_remove': drivers/mtd/nand/denali.c:1605: undefined reference to `dma_unmap_single' drivers/built-in.o: In function `denali_read_page_raw': drivers/mtd/nand/denali.c:1190: undefined reference to `dma_sync_single_for_cpu' drivers/built-in.o: In function `denali_read_page': drivers/mtd/nand/denali.c:1140: undefined reference to `dma_sync_single_for_cpu' drivers/built-in.o: In function `write_page': drivers/mtd/nand/denali.c:1051: undefined reference to `dma_sync_single_for_cpu' drivers/built-in.o: In function `denali_init': drivers/mtd/nand/denali.c:1433: undefined reference to `dma_set_mask' drivers/mtd/nand/denali.c:1438: undefined reference to `dma_map_single' drivers/mtd/nand/denali.c:1442: undefined reference to `dma_mapping_error' Signed-off-by: Geert Uytterhoeven Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 86c135b..e28021c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -43,6 +43,7 @@ config MTD_SM_COMMON config MTD_NAND_DENALI tristate "Support Denali NAND controller" + depends on HAS_DMA help Enable support for the Denali NAND controller. This should be combined with either the PCI or platform drivers to provide device -- cgit v0.10.2 From 8f229fb8566dd58004a84e2644c1f88ebe71bb88 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 12 Aug 2013 12:22:26 +0200 Subject: mtd: m25p80: remove unnecessary ifdef of_property_read_bool properly compiles away, no need to ifdef this for non DT builds. Signed-off-by: Sascha Hauer Acked-by: Marek Vasut Signed-off-by: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index a759c1f..35d5851 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1038,10 +1038,8 @@ static int m25p_probe(struct spi_device *spi) flash->mtd.writebufsize = flash->page_size; flash->fast_read = false; -#ifdef CONFIG_OF if (np && of_property_read_bool(np, "m25p,fast-read")) flash->fast_read = true; -#endif #ifdef CONFIG_M25PXX_USE_FAST_READ flash->fast_read = true; -- cgit v0.10.2 From 7e7d83b32ed7094f73008a99db4fecb74b8c2099 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 20 Aug 2013 09:54:39 +0200 Subject: mtd: m25p80: Pass flags through CAT25_INFO macro The flags may have to be overwritten, so add them to the CAT25_INFO macro. Signed-off-by: Sascha Hauer Acked-by: Marek Vasut Signed-off-by: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 35d5851..0b8672b 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -703,13 +703,13 @@ struct flash_info { .flags = (_flags), \ }) -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \ +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ ((kernel_ulong_t)&(struct flash_info) { \ .sector_size = (_sector_size), \ .n_sectors = (_n_sectors), \ .page_size = (_page_size), \ .addr_width = (_addr_width), \ - .flags = M25P_NO_ERASE, \ + .flags = (_flags), \ }) /* NOTE: double check command sets and memory organization when you add @@ -741,7 +741,7 @@ static const struct spi_device_id m25p_ids[] = { { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, /* Everspin */ - { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE) }, /* GigaDevice */ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, @@ -856,11 +856,11 @@ static const struct spi_device_id m25p_ids[] = { { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, /* Catalyst / On Semiconductor -- non-JEDEC */ - { "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, - { "cat25c03", CAT25_INFO( 32, 8, 16, 2) }, - { "cat25c09", CAT25_INFO( 128, 8, 32, 2) }, - { "cat25c17", CAT25_INFO( 256, 8, 32, 2) }, - { "cat25128", CAT25_INFO(2048, 8, 64, 2) }, + { "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE) }, + { "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE) }, + { "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE) }, + { "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE) }, { }, }; MODULE_DEVICE_TABLE(spi, m25p_ids); -- cgit v0.10.2 From 58146992c956c2104ca8990a621e39a20e9c9f94 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 20 Aug 2013 09:54:40 +0200 Subject: mtd: m25p80: make CONFIG_M25PXX_USE_FAST_READ safe to enable This patch adds a flag to struct flash_info indicating that fast_read is not supported. This now gives the following logic when determing whether to enable fastread: If the flash chip does not support fast_read, then disable it. Otherwise: 1) enable fast_read if device node contains m25p,fast-read 2) enable fast_read if forced in Kconfig This makes enabling CONFIG_M25PXX_USE_FAST_READ a safe option since we no longer enable the fast_read option unconditionally. For now fast_read is disabled for the everspin mr25h256 and the catalyst devices. Others may need the flag aswell. Signed-off-by: Sascha Hauer Acked-by: Marek Vasut Signed-off-by: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 0b8672b..6f93d32 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -691,6 +691,7 @@ struct flash_info { #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ #define M25P_NO_ERASE 0x02 /* No erase command needed */ #define SST_WRITE 0x04 /* use SST byte programming */ +#define M25P_NO_FR 0x08 /* Can't do fastread */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -741,7 +742,7 @@ static const struct spi_device_id m25p_ids[] = { { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, /* Everspin */ - { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE) }, + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, /* GigaDevice */ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, @@ -856,11 +857,11 @@ static const struct spi_device_id m25p_ids[] = { { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, /* Catalyst / On Semiconductor -- non-JEDEC */ - { "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE) }, - { "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE) }, - { "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE) }, - { "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE) }, - { "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE) }, + { "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) }, { }, }; MODULE_DEVICE_TABLE(spi, m25p_ids); @@ -1044,6 +1045,8 @@ static int m25p_probe(struct spi_device *spi) #ifdef CONFIG_M25PXX_USE_FAST_READ flash->fast_read = true; #endif + if (info->flags & M25P_NO_FR) + flash->fast_read = false; /* Default commands */ if (flash->fast_read) -- cgit v0.10.2 From a3d7ee9ff80949e8786072eaf0b267499aec3d05 Mon Sep 17 00:00:00 2001 From: Markus Niebel Date: Tue, 20 Aug 2013 09:54:41 +0200 Subject: mtd: m25p80: add support for mr25h10 This adds support for the Everspin mr25h10 MRAM chip to the m25p80 driver. Signed-off-by: Sascha Hauer Acked-by: Marek Vasut Signed-off-by: Artem Bityutskiy Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 6f93d32..a48f152 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -743,6 +743,7 @@ static const struct spi_device_id m25p_ids[] = { /* Everspin */ { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) }, /* GigaDevice */ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, -- cgit v0.10.2 From 582b2ffcaa4bd2f39801d6ffe9d076a5c555952a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 Aug 2013 16:10:21 +0900 Subject: mtd: sm_ftl: Staticize local symbols These local symbols are used only in this file. Fix the following sparse warnings: drivers/mtd/sm_ftl.c:25:25: warning: symbol 'cache_flush_workqueue' was not declared. Should it be static? drivers/mtd/sm_ftl.c:44:9: warning: symbol 'sm_attr_show' was not declared. Should it be static? drivers/mtd/sm_ftl.c:57:24: warning: symbol 'sm_create_sysfs_attributes' was not declared. Should it be static? drivers/mtd/sm_ftl.c:110:6: warning: symbol 'sm_delete_sysfs_attributes' was not declared. Should it be static? drivers/mtd/sm_ftl.c:574:5: warning: symbol 'sm_get_media_info' was not declared. Should it be static? drivers/mtd/sm_ftl.c:881:17: warning: symbol 'sm_get_zone' was not declared. Should it be static? drivers/mtd/sm_ftl.c:902:6: warning: symbol 'sm_cache_init' was not declared. Should it be static? drivers/mtd/sm_ftl.c:912:6: warning: symbol 'sm_cache_put' was not declared. Should it be static? drivers/mtd/sm_ftl.c:920:5: warning: symbol 'sm_cache_get' was not declared. Should it be static? drivers/mtd/sm_ftl.c:931:5: warning: symbol 'sm_cache_flush' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 3441e73..4b8e895 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -22,7 +22,7 @@ -struct workqueue_struct *cache_flush_workqueue; +static struct workqueue_struct *cache_flush_workqueue; static int cache_timeout = 1000; module_param(cache_timeout, int, S_IRUGO); @@ -41,7 +41,7 @@ struct sm_sysfs_attribute { int len; }; -ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, +static ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sm_sysfs_attribute *sm_attr = @@ -54,7 +54,7 @@ ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, #define NUM_ATTRIBUTES 1 #define SM_CIS_VENDOR_OFFSET 0x59 -struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) +static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) { struct attribute_group *attr_group; struct attribute **attributes; @@ -107,7 +107,7 @@ error1: return NULL; } -void sm_delete_sysfs_attributes(struct sm_ftl *ftl) +static void sm_delete_sysfs_attributes(struct sm_ftl *ftl) { struct attribute **attributes = ftl->disk_attributes->attrs; int i; @@ -571,7 +571,7 @@ static const uint8_t cis_signature[] = { }; /* Find out media parameters. * This ideally has to be based on nand id, but for now device size is enough */ -int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd) +static int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd) { int i; int size_in_megs = mtd->size / (1024 * 1024); @@ -878,7 +878,7 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num) } /* Get and automatically initialize an FTL mapping for one zone */ -struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) +static struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) { struct ftl_zone *zone; int error; @@ -899,7 +899,7 @@ struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) /* ----------------- cache handling ------------------------------------------*/ /* Initialize the one block cache */ -void sm_cache_init(struct sm_ftl *ftl) +static void sm_cache_init(struct sm_ftl *ftl) { ftl->cache_data_invalid_bitmap = 0xFFFFFFFF; ftl->cache_clean = 1; @@ -909,7 +909,7 @@ void sm_cache_init(struct sm_ftl *ftl) } /* Put sector in one block cache */ -void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) +static void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) { memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE); clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap); @@ -917,7 +917,7 @@ void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) } /* Read a sector from the cache */ -int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) +static int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) { if (test_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap)) @@ -928,7 +928,7 @@ int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) } /* Write the cache to hardware */ -int sm_cache_flush(struct sm_ftl *ftl) +static int sm_cache_flush(struct sm_ftl *ftl) { struct ftl_zone *zone; -- cgit v0.10.2 From d1d90c992da5b0b96cd6d3b2c0958b236808fb47 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 Aug 2013 16:12:14 +0900 Subject: mtd: impa7: add missing __iomem annotation Added missing __iomem annotation and used NULL instead of 0 in order to fix the following sparse warnings: drivers/mtd/maps/impa7.c:82:32: warning: Using plain integer as NULL pointer drivers/mtd/maps/impa7.c:96:34: warning: cast removes address space of expression drivers/mtd/maps/impa7.c:96:34: warning: incorrect type in argument 1 (different address spaces) drivers/mtd/maps/impa7.c:96:34: expected void volatile [noderef] *addr drivers/mtd/maps/impa7.c:96:34: got void * drivers/mtd/maps/impa7.c:108:34: warning: cast removes address space of expression drivers/mtd/maps/impa7.c:108:34: warning: incorrect type in argument 1 (different address spaces) drivers/mtd/maps/impa7.c:108:34: expected void volatile [noderef] *addr drivers/mtd/maps/impa7.c:108:34: got void * drivers/mtd/maps/impa7.c:109:45: warning: Using plain integer as NULL pointer Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index 4968674..15bbda0 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -79,7 +79,7 @@ static int __init init_impa7(void) } simple_map_init(&impa7_map[i]); - impa7_mtd[i] = 0; + impa7_mtd[i] = NULL; type = rom_probe_types; for(; !impa7_mtd[i] && *type; type++) { impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]); @@ -91,9 +91,9 @@ static int __init init_impa7(void) mtd_device_parse_register(impa7_mtd[i], NULL, NULL, partitions, ARRAY_SIZE(partitions)); + } else { + iounmap((void __iomem *)impa7_map[i].virt); } - else - iounmap((void *)impa7_map[i].virt); } return devicesfound == 0 ? -ENXIO : 0; } @@ -105,8 +105,8 @@ static void __exit cleanup_impa7(void) if (impa7_mtd[i]) { mtd_device_unregister(impa7_mtd[i]); map_destroy(impa7_mtd[i]); - iounmap((void *)impa7_map[i].virt); - impa7_map[i].virt = 0; + iounmap((void __iomem *)impa7_map[i].virt); + impa7_map[i].virt = NULL; } } } -- cgit v0.10.2 From b2b263f2e7f55a6bd5875a3cb7d0ab4bef433a7c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 Aug 2013 16:13:32 +0900 Subject: mtd: nandsim: Staticize local symbols These local symbols are used only in this file. Fix the following sparse warnings: drivers/mtd/nand/nandsim.c:1436:5: warning: symbol 'do_read_error' was not declared. Should it be static? drivers/mtd/nand/nandsim.c:1448:6: warning: symbol 'do_bit_flips' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 9e04de0..bdc1d15 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -1433,7 +1433,7 @@ static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; } -int do_read_error(struct nandsim *ns, int num) +static int do_read_error(struct nandsim *ns, int num) { unsigned int page_no = ns->regs.row; @@ -1445,7 +1445,7 @@ int do_read_error(struct nandsim *ns, int num) return 0; } -void do_bit_flips(struct nandsim *ns, int num) +static void do_bit_flips(struct nandsim *ns, int num) { if (bitflips && prandom_u32() < (1 << 22)) { int flips = 1; -- cgit v0.10.2 From 2fffc7982c990f089a4ab9bed10d90d1d657b3fc Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 Aug 2013 16:14:38 +0900 Subject: mtd: r852: Staticize local symbols These local symbols are used only in this file. Fix the following sparse warnings: drivers/mtd/nand/r852.c:232:6: warning: symbol 'r852_write_buf' was not declared. Should it be static? drivers/mtd/nand/r852.c:264:6: warning: symbol 'r852_read_buf' was not declared. Should it be static? drivers/mtd/nand/r852.c:315:6: warning: symbol 'r852_cmdctl' was not declared. Should it be static? drivers/mtd/nand/r852.c:360:5: warning: symbol 'r852_wait' was not declared. Should it be static? drivers/mtd/nand/r852.c:389:5: warning: symbol 'r852_ready' was not declared. Should it be static? drivers/mtd/nand/r852.c:400:6: warning: symbol 'r852_ecc_hwctl' was not declared. Should it be static? drivers/mtd/nand/r852.c:432:5: warning: symbol 'r852_ecc_calculate' was not declared. Should it be static? drivers/mtd/nand/r852.c:464:5: warning: symbol 'r852_ecc_correct' was not declared. Should it be static? drivers/mtd/nand/r852.c:532:6: warning: symbol 'r852_engine_enable' was not declared. Should it be static? drivers/mtd/nand/r852.c:550:6: warning: symbol 'r852_engine_disable' was not declared. Should it be static? drivers/mtd/nand/r852.c:560:6: warning: symbol 'r852_card_update_present' was not declared. Should it be static? drivers/mtd/nand/r852.c:575:6: warning: symbol 'r852_update_card_detect' was not declared. Should it be static? drivers/mtd/nand/r852.c:589:9: warning: symbol 'r852_media_type_show' was not declared. Should it be static? drivers/mtd/nand/r852.c:600:1: warning: symbol 'dev_attr_media_type' was not declared. Should it be static? drivers/mtd/nand/r852.c:604:6: warning: symbol 'r852_update_media_status' was not declared. Should it be static? drivers/mtd/nand/r852.c:633:5: warning: symbol 'r852_register_nand_device' was not declared. Should it be static? drivers/mtd/nand/r852.c:671:6: warning: symbol 'r852_unregister_nand_device' was not declared. Should it be static? drivers/mtd/nand/r852.c:685:6: warning: symbol 'r852_card_detect_work' was not declared. Should it be static? drivers/mtd/nand/r852.c:824:6: warning: symbol 'r852_probe' was not declared. Should it be static? drivers/mtd/nand/r852.c:964:6: warning: symbol 'r852_remove' was not declared. Should it be static? drivers/mtd/nand/r852.c:995:6: warning: symbol 'r852_shutdown' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index ef72884..9dcf02d 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -229,7 +229,7 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) /* * Program data lines of the nand chip to send data to it */ -void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +static void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { struct r852_device *dev = r852_get_dev(mtd); uint32_t reg; @@ -261,7 +261,7 @@ void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) /* * Read data lines of the nand chip to retrieve data */ -void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +static void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { struct r852_device *dev = r852_get_dev(mtd); uint32_t reg; @@ -312,7 +312,7 @@ static uint8_t r852_read_byte(struct mtd_info *mtd) /* * Control several chip lines & send commands */ -void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) +static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) { struct r852_device *dev = r852_get_dev(mtd); @@ -357,7 +357,7 @@ void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) * Wait till card is ready. * based on nand_wait, but returns errors on DMA error */ -int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) +static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct r852_device *dev = chip->priv; @@ -386,7 +386,7 @@ int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) * Check if card is ready */ -int r852_ready(struct mtd_info *mtd) +static int r852_ready(struct mtd_info *mtd) { struct r852_device *dev = r852_get_dev(mtd); return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY); @@ -397,7 +397,7 @@ int r852_ready(struct mtd_info *mtd) * Set ECC engine mode */ -void r852_ecc_hwctl(struct mtd_info *mtd, int mode) +static void r852_ecc_hwctl(struct mtd_info *mtd, int mode) { struct r852_device *dev = r852_get_dev(mtd); @@ -429,7 +429,7 @@ void r852_ecc_hwctl(struct mtd_info *mtd, int mode) * Calculate ECC, only used for writes */ -int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, +static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code) { struct r852_device *dev = r852_get_dev(mtd); @@ -461,7 +461,7 @@ int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, * Correct the data using ECC, hw did almost everything for us */ -int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, +static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) { uint16_t ecc_reg; @@ -529,7 +529,7 @@ static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, * Start the nand engine */ -void r852_engine_enable(struct r852_device *dev) +static void r852_engine_enable(struct r852_device *dev) { if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) { r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); @@ -547,7 +547,7 @@ void r852_engine_enable(struct r852_device *dev) * Stop the nand engine */ -void r852_engine_disable(struct r852_device *dev) +static void r852_engine_disable(struct r852_device *dev) { r852_write_reg_dword(dev, R852_HW, 0); r852_write_reg(dev, R852_CTL, R852_CTL_RESET); @@ -557,7 +557,7 @@ void r852_engine_disable(struct r852_device *dev) * Test if card is present */ -void r852_card_update_present(struct r852_device *dev) +static void r852_card_update_present(struct r852_device *dev) { unsigned long flags; uint8_t reg; @@ -572,7 +572,7 @@ void r852_card_update_present(struct r852_device *dev) * Update card detection IRQ state according to current card state * which is read in r852_card_update_present */ -void r852_update_card_detect(struct r852_device *dev) +static void r852_update_card_detect(struct r852_device *dev) { int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); dev->card_unstable = 0; @@ -586,8 +586,8 @@ void r852_update_card_detect(struct r852_device *dev) r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg); } -ssize_t r852_media_type_show(struct device *sys_dev, - struct device_attribute *attr, char *buf) +static ssize_t r852_media_type_show(struct device *sys_dev, + struct device_attribute *attr, char *buf) { struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev); struct r852_device *dev = r852_get_dev(mtd); @@ -597,11 +597,11 @@ ssize_t r852_media_type_show(struct device *sys_dev, return strlen(data); } -DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); +static DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); /* Detect properties of card in slot */ -void r852_update_media_status(struct r852_device *dev) +static void r852_update_media_status(struct r852_device *dev) { uint8_t reg; unsigned long flags; @@ -630,7 +630,7 @@ void r852_update_media_status(struct r852_device *dev) * Register the nand device * Called when the card is detected */ -int r852_register_nand_device(struct r852_device *dev) +static int r852_register_nand_device(struct r852_device *dev) { dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); @@ -668,7 +668,7 @@ error1: * Unregister the card */ -void r852_unregister_nand_device(struct r852_device *dev) +static void r852_unregister_nand_device(struct r852_device *dev) { if (!dev->card_registred) return; @@ -682,7 +682,7 @@ void r852_unregister_nand_device(struct r852_device *dev) } /* Card state updater */ -void r852_card_detect_work(struct work_struct *work) +static void r852_card_detect_work(struct work_struct *work) { struct r852_device *dev = container_of(work, struct r852_device, card_detect_work.work); @@ -821,7 +821,7 @@ out: return ret; } -int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { int error; struct nand_chip *chip; @@ -961,7 +961,7 @@ error1: return error; } -void r852_remove(struct pci_dev *pci_dev) +static void r852_remove(struct pci_dev *pci_dev) { struct r852_device *dev = pci_get_drvdata(pci_dev); @@ -992,7 +992,7 @@ void r852_remove(struct pci_dev *pci_dev) pci_disable_device(pci_dev); } -void r852_shutdown(struct pci_dev *pci_dev) +static void r852_shutdown(struct pci_dev *pci_dev) { struct r852_device *dev = pci_get_drvdata(pci_dev); -- cgit v0.10.2 From e51fb2cb2b6e1ea2c99ad5dbd332f2524a21b68a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 Aug 2013 16:16:36 +0900 Subject: mtd: spear_smi: add missing __iomem annotation Added missing __iomem annotation in order to fix the following sparse warnings: drivers/mtd/devices/spear_smi.c:566:13: warning: incorrect type in assignment (different address spaces) drivers/mtd/devices/spear_smi.c:566:13: expected void *src drivers/mtd/devices/spear_smi.c:566:13: got void [noderef] * drivers/mtd/devices/spear_smi.c:586:9: warning: incorrect type in argument 2 (different address spaces) drivers/mtd/devices/spear_smi.c:586:9: expected void const volatile [noderef] * drivers/mtd/devices/spear_smi.c:586:9: got unsigned char [usertype] * drivers/mtd/devices/spear_smi.c:659:14: warning: incorrect type in assignment (different address spaces) drivers/mtd/devices/spear_smi.c:659:14: expected void *dest drivers/mtd/devices/spear_smi.c:659:14: got void [noderef] * drivers/mtd/devices/spear_smi.c:620:9: warning: incorrect type in argument 1 (different address spaces) drivers/mtd/devices/spear_smi.c:620:9: expected void volatile [noderef] * drivers/mtd/devices/spear_smi.c:620:9: got void *dest drivers/mtd/devices/spear_smi.c:620:9: warning: incorrect type in argument 1 (different address spaces) drivers/mtd/devices/spear_smi.c:620:9: expected void volatile [noderef] * drivers/mtd/devices/spear_smi.c:620:9: got void *dest drivers/mtd/devices/spear_smi.c:620:9: warning: incorrect type in argument 1 (different address spaces) drivers/mtd/devices/spear_smi.c:620:9: expected void volatile [noderef] * drivers/mtd/devices/spear_smi.c:620:9: got void *dest Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 092bb68..4238214 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -550,7 +550,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, { struct spear_snor_flash *flash = get_flash_data(mtd); struct spear_smi *dev = mtd->priv; - void *src; + void __iomem *src; u32 ctrlreg1, val; int ret; @@ -583,7 +583,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, writel(val, dev->io_base + SMI_CR1); - memcpy_fromio(buf, (u8 *)src, len); + memcpy_fromio(buf, src, len); /* restore ctrl reg1 */ writel(ctrlreg1, dev->io_base + SMI_CR1); @@ -596,7 +596,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, } static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank, - void *dest, const void *src, size_t len) + void __iomem *dest, const void *src, size_t len) { int ret; u32 ctrlreg1; @@ -643,7 +643,7 @@ static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, { struct spear_snor_flash *flash = get_flash_data(mtd); struct spear_smi *dev = mtd->priv; - void *dest; + void __iomem *dest; u32 page_offset, page_size; int ret; -- cgit v0.10.2 From 9a589c8bad41d117184c0b7d658956d525354af5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 Aug 2013 16:17:29 +0900 Subject: mtd: maps: cfi_flagadm: add missing __iomem annotation Added missing __iomem annotation and staticized local symbols in order to fix the following sparse warnings: drivers/mtd/maps/cfi_flagadm.c:58:17: warning: symbol 'flagadm_map' was not declared. Should it be static? drivers/mtd/maps/cfi_flagadm.c:64:22: warning: symbol 'flagadm_parts' was not declared. Should it be static? drivers/mtd/maps/cfi_flagadm.c:115:18: warning: cast removes address space of expression drivers/mtd/maps/cfi_flagadm.c:115:18: warning: incorrect type in argument 1 (different address spaces) drivers/mtd/maps/cfi_flagadm.c:115:18: expected void volatile [noderef] *addr drivers/mtd/maps/cfi_flagadm.c:115:18: got void * drivers/mtd/maps/cfi_flagadm.c:126:26: warning: cast removes address space of expression drivers/mtd/maps/cfi_flagadm.c:126:26: warning: incorrect type in argument 1 (different address spaces) drivers/mtd/maps/cfi_flagadm.c:126:26: expected void volatile [noderef] *addr drivers/mtd/maps/cfi_flagadm.c:126:26: got void * drivers/mtd/maps/cfi_flagadm.c:127:36: warning: Using plain integer as NULL pointer Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index d16fc9d..d504b3d 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -55,13 +55,13 @@ #define FLASH_PARTITION3_SIZE 0x001C0000 -struct map_info flagadm_map = { +static struct map_info flagadm_map = { .name = "FlagaDM flash device", .size = FLASH_SIZE, .bankwidth = 2, }; -struct mtd_partition flagadm_parts[] = { +static struct mtd_partition flagadm_parts[] = { { .name = "Bootloader", .offset = FLASH_PARTITION0_ADDR, @@ -112,7 +112,7 @@ static int __init init_flagadm(void) return 0; } - iounmap((void *)flagadm_map.virt); + iounmap((void __iomem *)flagadm_map.virt); return -ENXIO; } @@ -123,8 +123,8 @@ static void __exit cleanup_flagadm(void) map_destroy(mymtd); } if (flagadm_map.virt) { - iounmap((void *)flagadm_map.virt); - flagadm_map.virt = 0; + iounmap((void __iomem *)flagadm_map.virt); + flagadm_map.virt = NULL; } } -- cgit v0.10.2 From b2ac0376107b9350fbaf5abd94d33c4a07aa86c0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 7 Aug 2013 16:18:52 +0900 Subject: mtd: nand: mxc_nand: mark 'const' properly The values pointed by the pointer are used as read-only. Also, mtd_device_parse_register() uses 'part_probes[]' as the second argument which is defined as 'const char * const *types'. Thus, the 'const' should be moved to be after the '*'. drivers/mtd/nand/mxc_nand.c:269:25: warning: duplicate const Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index a99ff1d..ce8242b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -266,7 +266,7 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = { } }; -static const char const *part_probes[] = { +static const char * const part_probes[] = { "cmdlinepart", "RedBoot", "ofpart", NULL }; static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) -- cgit v0.10.2 From ebc6e4b6525872498fb01f5ad9c29a81b9d49831 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Tue, 6 Aug 2013 15:03:29 +0300 Subject: mtd: remove alauda driver The driver has very low utility. Devices in question are limited to about 400kB/s and the only known user (me) discarded the hardware several years back. Signed-off-by: Joern Engel Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index e28021c..d885298 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -433,13 +433,6 @@ config MTD_NAND_PLATFORM devices. You will need to provide platform-specific functions via platform_data. -config MTD_ALAUDA - tristate "MTD driver for Olympus MAUSB-10 and Fujifilm DPC-R1" - depends on USB - help - These two (and possibly other) Alauda-based cardreaders for - SmartMedia and xD allow raw flash access. - config MTD_NAND_ORION tristate "NAND Flash support for Marvell Orion SoC" depends on PLAT_ORION diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index bb81891..542b568 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o -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 diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c deleted file mode 100644 index 60a0dfd..0000000 --- a/drivers/mtd/nand/alauda.c +++ /dev/null @@ -1,723 +0,0 @@ -/* - * MTD driver for Alauda chips - * - * Copyright (C) 2007 Joern Engel - * - * Based on drivers/usb/usb-skeleton.c which is: - * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) - * and on drivers/usb/storage/alauda.c, which is: - * (c) 2005 Daniel Drake - * - * Idea and initial work by Arnd Bergmann - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Control commands */ -#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 -#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a -#define ALAUDA_GET_XD_MEDIA_SIG 0x86 - -/* Common prefix */ -#define ALAUDA_BULK_CMD 0x40 - -/* The two ports */ -#define ALAUDA_PORT_XD 0x00 -#define ALAUDA_PORT_SM 0x01 - -/* Bulk commands */ -#define ALAUDA_BULK_READ_PAGE 0x84 -#define ALAUDA_BULK_READ_OOB 0x85 /* don't use, there's a chip bug */ -#define ALAUDA_BULK_READ_BLOCK 0x94 -#define ALAUDA_BULK_ERASE_BLOCK 0xa3 -#define ALAUDA_BULK_WRITE_PAGE 0xa4 -#define ALAUDA_BULK_WRITE_BLOCK 0xb4 -#define ALAUDA_BULK_RESET_MEDIA 0xe0 - -/* Address shifting */ -#define PBA_LO(pba) ((pba & 0xF) << 5) -#define PBA_HI(pba) (pba >> 3) -#define PBA_ZONE(pba) (pba >> 11) - -#define TIMEOUT HZ - -static const struct usb_device_id alauda_table[] = { - { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ - { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ - { } -}; -MODULE_DEVICE_TABLE(usb, alauda_table); - -struct alauda_card { - u8 id; /* id byte */ - u8 chipshift; /* 1<id; card++) - if (card->id == id) - return card; - return NULL; -} - -static void alauda_delete(struct kref *kref) -{ - struct alauda *al = container_of(kref, struct alauda, kref); - - if (al->mtd) { - mtd_device_unregister(al->mtd); - kfree(al->mtd); - } - usb_put_dev(al->dev); - kfree(al); -} - -static int alauda_get_media_status(struct alauda *al, void *buf) -{ - int ret; - - mutex_lock(&al->card_mutex); - ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), - ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ); - mutex_unlock(&al->card_mutex); - return ret; -} - -static int alauda_ack_media(struct alauda *al) -{ - int ret; - - mutex_lock(&al->card_mutex); - ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0), - ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ); - mutex_unlock(&al->card_mutex); - return ret; -} - -static int alauda_get_media_signatures(struct alauda *al, void *buf) -{ - int ret; - - mutex_lock(&al->card_mutex); - ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), - ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ); - mutex_unlock(&al->card_mutex); - return ret; -} - -static void alauda_reset(struct alauda *al) -{ - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0, - 0, 0, 0, 0, al->port - }; - mutex_lock(&al->card_mutex); - usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ); - mutex_unlock(&al->card_mutex); -} - -static void correct_data(void *buf, void *read_ecc, - int *corrected, int *uncorrected) -{ - u8 calc_ecc[3]; - int err; - - nand_calculate_ecc(NULL, buf, calc_ecc); - err = nand_correct_data(NULL, buf, read_ecc, calc_ecc); - if (err) { - if (err > 0) - (*corrected)++; - else - (*uncorrected)++; - } -} - -struct alauda_sg_request { - struct urb *urb[3]; - struct completion comp; -}; - -static void alauda_complete(struct urb *urb) -{ - struct completion *comp = urb->context; - - if (comp) - complete(comp); -} - -static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf, - void *oob) -{ - struct alauda_sg_request sg; - struct alauda *al = mtd->priv; - u32 pba = from >> al->card->blockshift; - u32 page = (from >> al->card->pageshift) & al->pagemask; - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba), - PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port - }; - int i, err; - - for (i=0; i<3; i++) - sg.urb[i] = NULL; - - err = -ENOMEM; - for (i=0; i<3; i++) { - sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); - if (!sg.urb[i]) - goto out; - } - init_completion(&sg.comp); - usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16, - alauda_complete, &sg.comp); - - mutex_lock(&al->card_mutex); - for (i=0; i<3; i++) { - err = usb_submit_urb(sg.urb[i], GFP_NOIO); - if (err) - goto cancel; - } - if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { - err = -ETIMEDOUT; -cancel: - for (i=0; i<3; i++) { - usb_kill_urb(sg.urb[i]); - } - } - mutex_unlock(&al->card_mutex); - -out: - usb_free_urb(sg.urb[0]); - usb_free_urb(sg.urb[1]); - usb_free_urb(sg.urb[2]); - return err; -} - -static int alauda_read_page(struct mtd_info *mtd, loff_t from, - void *buf, u8 *oob, int *corrected, int *uncorrected) -{ - int err; - - err = __alauda_read_page(mtd, from, buf, oob); - if (err) - return err; - correct_data(buf, oob+13, corrected, uncorrected); - correct_data(buf+256, oob+8, corrected, uncorrected); - return 0; -} - -static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf, - void *oob) -{ - struct alauda_sg_request sg; - struct alauda *al = mtd->priv; - u32 pba = to >> al->card->blockshift; - u32 page = (to >> al->card->pageshift) & al->pagemask; - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba), - PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port - }; - int i, err; - - for (i=0; i<3; i++) - sg.urb[i] = NULL; - - err = -ENOMEM; - for (i=0; i<3; i++) { - sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); - if (!sg.urb[i]) - goto out; - } - init_completion(&sg.comp); - usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16, - alauda_complete, &sg.comp); - - mutex_lock(&al->card_mutex); - for (i=0; i<3; i++) { - err = usb_submit_urb(sg.urb[i], GFP_NOIO); - if (err) - goto cancel; - } - if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { - err = -ETIMEDOUT; -cancel: - for (i=0; i<3; i++) { - usb_kill_urb(sg.urb[i]); - } - } - mutex_unlock(&al->card_mutex); - -out: - usb_free_urb(sg.urb[0]); - usb_free_urb(sg.urb[1]); - usb_free_urb(sg.urb[2]); - return err; -} - -static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs) -{ - struct alauda_sg_request sg; - struct alauda *al = mtd->priv; - u32 pba = ofs >> al->card->blockshift; - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), - PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port - }; - u8 buf[2]; - int i, err; - - for (i=0; i<2; i++) - sg.urb[i] = NULL; - - err = -ENOMEM; - for (i=0; i<2; i++) { - sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); - if (!sg.urb[i]) - goto out; - } - init_completion(&sg.comp); - usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2, - alauda_complete, &sg.comp); - - mutex_lock(&al->card_mutex); - for (i=0; i<2; i++) { - err = usb_submit_urb(sg.urb[i], GFP_NOIO); - if (err) - goto cancel; - } - if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { - err = -ETIMEDOUT; -cancel: - for (i=0; i<2; i++) { - usb_kill_urb(sg.urb[i]); - } - } - mutex_unlock(&al->card_mutex); - -out: - usb_free_urb(sg.urb[0]); - usb_free_urb(sg.urb[1]); - return err; -} - -static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob) -{ - static u8 ignore_buf[512]; /* write only */ - - return __alauda_read_page(mtd, from, ignore_buf, oob); -} - -static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) -{ - u8 oob[16]; - int err; - - err = alauda_read_oob(mtd, ofs, oob); - if (err) - return err; - - /* A block is marked bad if two or more bits are zero */ - return hweight8(oob[5]) >= 7 ? 0 : 1; -} - -static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct alauda *al = mtd->priv; - void *bounce_buf; - int err, corrected=0, uncorrected=0; - - bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL); - if (!bounce_buf) - return -ENOMEM; - - *retlen = len; - while (len) { - u8 oob[16]; - size_t byte = from & al->bytemask; - size_t cplen = min(len, mtd->writesize - byte); - - err = alauda_read_page(mtd, from, bounce_buf, oob, - &corrected, &uncorrected); - if (err) - goto out; - - memcpy(buf, bounce_buf + byte, cplen); - buf += cplen; - from += cplen; - len -= cplen; - } - err = 0; - if (corrected) - err = 1; /* return max_bitflips per ecc step */ - if (uncorrected) - err = -EBADMSG; -out: - kfree(bounce_buf); - return err; -} - -static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct alauda *al = mtd->priv; - int err, corrected=0, uncorrected=0; - - if ((from & al->bytemask) || (len & al->bytemask)) - return alauda_bounce_read(mtd, from, len, retlen, buf); - - *retlen = len; - while (len) { - u8 oob[16]; - - err = alauda_read_page(mtd, from, buf, oob, - &corrected, &uncorrected); - if (err) - return err; - - buf += mtd->writesize; - from += mtd->writesize; - len -= mtd->writesize; - } - err = 0; - if (corrected) - err = 1; /* return max_bitflips per ecc step */ - if (uncorrected) - err = -EBADMSG; - return err; -} - -static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct alauda *al = mtd->priv; - int err; - - if ((to & al->bytemask) || (len & al->bytemask)) - return -EINVAL; - - *retlen = len; - while (len) { - u32 page = (to >> al->card->pageshift) & al->pagemask; - u8 oob[16] = { 'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - - /* don't write to bad blocks */ - if (page == 0) { - err = alauda_isbad(mtd, to); - if (err) { - return -EIO; - } - } - nand_calculate_ecc(mtd, buf, &oob[13]); - nand_calculate_ecc(mtd, buf+256, &oob[8]); - - err = alauda_write_page(mtd, to, (void*)buf, oob); - if (err) - return err; - - buf += mtd->writesize; - to += mtd->writesize; - len -= mtd->writesize; - } - return 0; -} - -static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct alauda *al = mtd->priv; - u32 ofs = instr->addr; - u32 len = instr->len; - int err; - - if ((ofs & al->blockmask) || (len & al->blockmask)) - return -EINVAL; - - while (len) { - /* don't erase bad blocks */ - err = alauda_isbad(mtd, ofs); - if (err > 0) - err = -EIO; - if (err < 0) - return err; - - err = alauda_erase_block(mtd, ofs); - if (err < 0) - return err; - - ofs += mtd->erasesize; - len -= mtd->erasesize; - } - return 0; -} - -static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - int err; - - err = __alauda_erase(mtd, instr); - instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE; - mtd_erase_callback(instr); - return err; -} - -static int alauda_init_media(struct alauda *al) -{ - u8 buf[4], *b0=buf, *b1=buf+1; - struct alauda_card *card; - struct mtd_info *mtd; - int err; - - mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); - if (!mtd) - return -ENOMEM; - - for (;;) { - err = alauda_get_media_status(al, buf); - if (err < 0) - goto error; - if (*b0 & 0x10) - break; - msleep(20); - } - - err = alauda_ack_media(al); - if (err) - goto error; - - msleep(10); - - err = alauda_get_media_status(al, buf); - if (err < 0) - goto error; - - if (*b0 != 0x14) { - /* media not ready */ - err = -EIO; - goto error; - } - err = alauda_get_media_signatures(al, buf); - if (err < 0) - goto error; - - card = get_card(*b1); - if (!card) { - printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1); - err = -EIO; - goto error; - } - printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n", - 1<pageshift, 1<blockshift, - 1<<(card->chipshift-20)); - al->card = card; - al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1; - al->bytemask = (1 << card->pageshift) - 1; - al->blockmask = (1 << card->blockshift) - 1; - - mtd->name = "alauda"; - mtd->size = 1<chipshift; - mtd->erasesize = 1<blockshift; - mtd->writesize = 1<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->priv = al; - mtd->owner = THIS_MODULE; - mtd->ecc_strength = 1; - - err = mtd_device_register(mtd, NULL, 0); - if (err) { - err = -ENFILE; - goto error; - } - - al->mtd = mtd; - alauda_reset(al); /* no clue whether this is necessary */ - return 0; -error: - kfree(mtd); - return err; -} - -static int alauda_check_media(struct alauda *al) -{ - u8 buf[2], *b0 = buf, *b1 = buf+1; - int err; - - err = alauda_get_media_status(al, buf); - if (err < 0) - return err; - - if ((*b1 & 0x01) == 0) { - /* door open */ - return -EIO; - } - if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) { - /* no media ? */ - return -EIO; - } - if (*b0 & 0x08) { - /* media change ? */ - return alauda_init_media(al); - } - return 0; -} - -static int alauda_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct alauda *al; - struct usb_host_interface *iface; - struct usb_endpoint_descriptor *ep, - *ep_in=NULL, *ep_out=NULL, *ep_wr=NULL; - int i, err = -ENOMEM; - - al = kzalloc(2*sizeof(*al), GFP_KERNEL); - if (!al) - goto error; - - kref_init(&al->kref); - usb_set_intfdata(interface, al); - - al->dev = usb_get_dev(interface_to_usbdev(interface)); - al->interface = interface; - - iface = interface->cur_altsetting; - for (i = 0; i < iface->desc.bNumEndpoints; ++i) { - ep = &iface->endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(ep)) { - ep_in = ep; - } else if (usb_endpoint_is_bulk_out(ep)) { - if (i==0) - ep_wr = ep; - else - ep_out = ep; - } - } - err = -EIO; - if (!ep_wr || !ep_in || !ep_out) - goto error; - - al->write_out = usb_sndbulkpipe(al->dev, - usb_endpoint_num(ep_wr)); - al->bulk_in = usb_rcvbulkpipe(al->dev, - usb_endpoint_num(ep_in)); - al->bulk_out = usb_sndbulkpipe(al->dev, - usb_endpoint_num(ep_out)); - - /* second device is identical up to now */ - memcpy(al+1, al, sizeof(*al)); - - mutex_init(&al[0].card_mutex); - mutex_init(&al[1].card_mutex); - - al[0].port = ALAUDA_PORT_XD; - al[1].port = ALAUDA_PORT_SM; - - dev_info(&interface->dev, "alauda probed\n"); - alauda_check_media(al); - alauda_check_media(al+1); - - return 0; - -error: - if (al) - kref_put(&al->kref, alauda_delete); - return err; -} - -static void alauda_disconnect(struct usb_interface *interface) -{ - struct alauda *al; - - al = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - /* FIXME: prevent more I/O from starting */ - - /* decrement our usage count */ - if (al) - kref_put(&al->kref, alauda_delete); - - dev_info(&interface->dev, "alauda gone"); -} - -static struct usb_driver alauda_driver = { - .name = "alauda", - .probe = alauda_probe, - .disconnect = alauda_disconnect, - .id_table = alauda_table, -}; - -module_usb_driver(alauda_driver); - -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From d2b1bd1422d9385cc5f463e3cc6ea0cbe7e98b70 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Thu, 30 May 2013 10:22:12 +0800 Subject: mtd: bcm47xxsflash: convert kzalloc to avoid invalid access mtd is just member of bcm47xxsflash, so we should free bcm47xxsflash not its member. So I use devm_kazlloc instead of kazlloc to avoid it. * Changelog: convert to devm_kzalloc Signed-off-by: Libo chen Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko [Brian: fixed conflicts] Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 2060856..fa56a47 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -117,11 +117,9 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) struct bcm47xxsflash *b47s; int err; - b47s = kzalloc(sizeof(*b47s), GFP_KERNEL); - if (!b47s) { - err = -ENOMEM; - goto out; - } + b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL); + if (!b47s) + return -ENOMEM; sflash->priv = b47s; b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash); @@ -146,18 +144,13 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); if (err) { pr_err("Failed to register MTD device: %d\n", err); - goto err_dev_reg; + return err; } if (bcm47xxsflash_poll(b47s, HZ / 10)) pr_warn("Serial flash busy\n"); return 0; - -err_dev_reg: - kfree(&b47s->mtd); -out: - return err; } static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) @@ -166,7 +159,6 @@ static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) struct bcm47xxsflash *b47s = sflash->priv; mtd_device_unregister(&b47s->mtd); - kfree(b47s); return 0; } -- cgit v0.10.2 From 8268df26369f7779d3394ec066d72cc00232efe1 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Thu, 30 May 2013 10:22:16 +0800 Subject: mtd: bcm47xxsflash: convert to module_platform_driver instead of init/exit convert to module_platform_driver instead of init/exit Suggested-by: Andy Shevchenko Signed-off-by: Libo chen Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index fa56a47..e9e3887 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -176,22 +176,4 @@ static struct platform_driver bcma_sflash_driver = { * Init **************************************************/ -static int __init bcm47xxsflash_init(void) -{ - int err; - - err = platform_driver_register(&bcma_sflash_driver); - if (err) - pr_err("Failed to register BCMA serial flash driver: %d\n", - err); - - return err; -} - -static void __exit bcm47xxsflash_exit(void) -{ - platform_driver_unregister(&bcma_sflash_driver); -} - -module_init(bcm47xxsflash_init); -module_exit(bcm47xxsflash_exit); +module_platform_driver(bcma_sflash_driver); -- cgit v0.10.2 From c0fcbc56d93220c772a524e49ed2612fe5787a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 21 May 2013 21:03:46 +0200 Subject: mtd: bcm47xxsflash: implement erasing support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index e9e3887..534cb74 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -64,6 +64,42 @@ static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout) * MTD ops **************************************************/ +static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + struct bcm47xxsflash *b47s = mtd->priv; + int err; + + switch (b47s->type) { + case BCM47XXSFLASH_TYPE_ST: + bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN); + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr); + /* Newer flashes have "sub-sectors" which can be erased + * independently with a new command: ST_SSE. The ST_SE command + * erases 64KB just as before. + */ + if (b47s->blocksize < (64 * 1024)) + bcm47xxsflash_cmd(b47s, OPCODE_ST_SSE); + else + bcm47xxsflash_cmd(b47s, OPCODE_ST_SE); + break; + case BCM47XXSFLASH_TYPE_ATMEL: + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr << 1); + bcm47xxsflash_cmd(b47s, OPCODE_AT_PAGE_ERASE); + break; + } + + err = bcm47xxsflash_poll(b47s, HZ); + if (err) + erase->state = MTD_ERASE_FAILED; + else + erase->state = MTD_ERASE_DONE; + + if (erase->callback) + erase->callback(erase); + + return err; +} + static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -88,12 +124,15 @@ static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) mtd->name = "bcm47xxsflash"; mtd->owner = THIS_MODULE; mtd->type = MTD_ROM; - mtd->size = b47s->size; - mtd->_read = bcm47xxsflash_read; /* TODO: implement writing support and verify/change following code */ mtd->flags = MTD_CAP_ROM; + mtd->size = b47s->size; + mtd->erasesize = b47s->blocksize; mtd->writebufsize = mtd->writesize = 1; + + mtd->_erase = bcm47xxsflash_erase; + mtd->_read = bcm47xxsflash_read; } /************************************************** -- cgit v0.10.2 From 13134f48c8a7a9daaef74118d19f2d2ef29cd33a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 22 May 2013 14:39:02 +0200 Subject: mtd: bcm47xxsflash: writing support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested with BCM4706. Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 534cb74..77de29b 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -116,6 +116,127 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, return len; } +static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len, + const u_char *buf) +{ + struct bcm47xxsflash *b47s = mtd->priv; + int written = 0; + + /* Enable writes */ + bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN); + + /* Write first byte */ + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, offset); + b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++); + + /* Program page */ + if (b47s->bcma_cc->core->id.rev < 20) { + bcm47xxsflash_cmd(b47s, OPCODE_ST_PP); + return 1; /* 1B written */ + } + + /* Program page and set CSA (on newer chips we can continue writing) */ + bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | OPCODE_ST_PP); + offset++; + len--; + written++; + + while (len > 0) { + /* Page boundary, another function call is needed */ + if ((offset & 0xFF) == 0) + break; + + bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | *buf++); + offset++; + len--; + written++; + } + + /* All done, drop CSA & poll */ + b47s->cc_write(b47s, BCMA_CC_FLASHCTL, 0); + udelay(1); + if (bcm47xxsflash_poll(b47s, HZ / 10)) + pr_err("Flash rejected dropping CSA\n"); + + return written; +} + +static int bcm47xxsflash_write_at(struct mtd_info *mtd, u32 offset, size_t len, + const u_char *buf) +{ + struct bcm47xxsflash *b47s = mtd->priv; + u32 mask = b47s->blocksize - 1; + u32 page = (offset & ~mask) << 1; + u32 byte = offset & mask; + int written = 0; + + /* If we don't overwrite whole page, read it to the buffer first */ + if (byte || (len < b47s->blocksize)) { + int err; + + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page); + bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_LOAD); + /* 250 us for AT45DB321B */ + err = bcm47xxsflash_poll(b47s, HZ / 1000); + if (err) { + pr_err("Timeout reading page 0x%X info buffer\n", page); + return err; + } + } + + /* Change buffer content with our data */ + while (len > 0) { + /* Page boundary, another function call is needed */ + if (byte == b47s->blocksize) + break; + + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, byte++); + b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++); + bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_WRITE); + len--; + written++; + } + + /* Program page with the buffer content */ + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page); + bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_PROGRAM); + + return written; +} + +static int bcm47xxsflash_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct bcm47xxsflash *b47s = mtd->priv; + int written; + + /* Writing functions can return without writing all passed data, for + * example when the hardware is too old or when we git page boundary. + */ + while (len > 0) { + switch (b47s->type) { + case BCM47XXSFLASH_TYPE_ST: + written = bcm47xxsflash_write_st(mtd, to, len, buf); + break; + case BCM47XXSFLASH_TYPE_ATMEL: + written = bcm47xxsflash_write_at(mtd, to, len, buf); + break; + default: + BUG_ON(1); + } + if (written < 0) { + pr_err("Error writing at offset 0x%llX\n", to); + return written; + } + to += (loff_t)written; + len -= written; + *retlen += written; + buf += written; + } + + return 0; +} + static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) { struct mtd_info *mtd = &b47s->mtd; @@ -123,16 +244,17 @@ static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) mtd->priv = b47s; mtd->name = "bcm47xxsflash"; mtd->owner = THIS_MODULE; - mtd->type = MTD_ROM; - /* TODO: implement writing support and verify/change following code */ - mtd->flags = MTD_CAP_ROM; + mtd->type = MTD_NORFLASH; + mtd->flags = MTD_CAP_NORFLASH; mtd->size = b47s->size; mtd->erasesize = b47s->blocksize; - mtd->writebufsize = mtd->writesize = 1; + mtd->writesize = 1; + mtd->writebufsize = 1; mtd->_erase = bcm47xxsflash_erase; mtd->_read = bcm47xxsflash_read; + mtd->_write = bcm47xxsflash_write; } /************************************************** -- cgit v0.10.2 From ff52c67a1048fef9b381b98e17fcaca01b076190 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 23 Aug 2013 10:50:36 +0800 Subject: mtd: atmel_nand: fix error return code in atmel_nand_probe() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Acked-by: Josh Wu Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 8b2eb3e..060feea 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -2048,6 +2048,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(host->dev, "Cannot get HSMC irq!\n"); + res = irq; goto err_nand_ioremap; } -- cgit v0.10.2 From 8e7fd23686b49c186a15343c82a0a54c3a406234 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Aug 2013 12:51:45 +0300 Subject: mtd cs553x_nand: use kzalloc() instead of memset It's cleaner to use kzalloc() instead of zeroing out in a separate call to memset(). Signed-off-by: Dan Carpenter Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 2cdeab8..d469a9a 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -197,7 +197,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) } /* Allocate memory for MTD device structure and private data */ - new_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + new_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!new_mtd) { printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n"); err = -ENOMEM; @@ -207,10 +207,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) /* Get pointer to private data */ this = (struct nand_chip *)(&new_mtd[1]); - /* Initialize structures */ - memset(new_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ new_mtd->priv = this; new_mtd->owner = THIS_MODULE; -- cgit v0.10.2 From 3156231fb644cf29c1d7f21766ff9e33dc9761d3 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 19 Jul 2013 15:54:11 +0900 Subject: mtd: mtdswap: replace strict_strtoul() with kstrtoul() The usage of strict_strtoul() is not preferred, because strict_strtoul() is obsolete. Thus, kstrtoul() should be used. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index c92f0f6..8b33b26 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -1425,7 +1425,7 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) return; while ((this_opt = strsep(&parts, ",")) != NULL) { - if (strict_strtoul(this_opt, 0, &part) < 0) + if (kstrtoul(this_opt, 0, &part) < 0) return; if (mtd->index == part) -- cgit v0.10.2 From 60ea89e22aa5de529d0fdb9de07504afa8e5d4b0 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 23 Aug 2013 11:04:27 +0800 Subject: mtd: ofpart: use for_each_child_of_node() macro Use for_each_child_of_node() macro instead of open coding it. Signed-off-by: Wei Yongjun Acked-by: Huang Shijie Signed-off-by: David Woodhouse diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 7843a44..d64f8c3 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -43,9 +43,8 @@ static int parse_ofpart_partitions(struct mtd_info *master, return 0; /* First count the subnodes */ - pp = NULL; nr_parts = 0; - while ((pp = of_get_next_child(node, pp))) { + for_each_child_of_node(node, pp) { if (node_has_compatible(pp)) continue; @@ -59,9 +58,8 @@ static int parse_ofpart_partitions(struct mtd_info *master, if (!*pparts) return -ENOMEM; - pp = NULL; i = 0; - while ((pp = of_get_next_child(node, pp))) { + for_each_child_of_node(node, pp) { const __be32 *reg; int len; int a_cells, s_cells; -- cgit v0.10.2 From 6c3b88970175e18a67eb8e55c4eba10614d0d5dc Mon Sep 17 00:00:00 2001 From: Michel Stempin Date: Mon, 15 Jul 2013 12:13:56 +0200 Subject: mtd: chips: Add support for PMC SPI Flash chips in m25p80.c Add support for PMC (now Chingis, part of ISSI) Pm25LV512 (512 Kib), Pm25LV010 (1 Mib) and Pm25LQ032 (32 Mib) SPI Flash chips. This patch addresses two generations of PMC SPI Flash chips: - Pm25LV512 and Pm25LV010: these have 4KiB sectors and 32KiB blocks. The 4KiB sector erase uses a non-standard opcode (0xd7). They do not support JEDEC RDID (0x9f), and so they can only be detected by matching their name string with pre-configured platform data. Because of the cascaded acquisitions, the datasheet is no longer available on the current manufacturer's website, although it is still commonly used in some recent wireless routers (). The only public datasheet available seems to be on GeoCities: - Pm25LQ032: a newer generation flash, with 4KiB sectors and 64KiB blocks. It uses the standard erase and JEDEC read-ID opcodes. Manufacturer's datasheet is here: This patch is resent in order to take into account both Brian Norris remarks and this upstream patch: commit e534ee4f9ca29fdb38eea4b0c53f2154fbd8c1ee Author: Krzysztof Mazur Date: Fri Feb 22 15:51:05 2013 +0100 mtd: m25p80: introduce SST_WRITE flag for SST byte programming Not all SST devices implement the SST byte programming command. Some devices (like SST25VF064C) implement only standard m25p80 page write command. Now SPI flash devices that need sst_write() are explicitly marked with new SST_WRITE flag and the decision to use sst_write() is based on this flag instead of manufacturer id. Signed-off-by: Gabor Juhos Signed-off-by: Michel Stempin [Brian: fixed conflict] Signed-off-by: Brian Norris Signed-off-by: David Woodhouse diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index a48f152..26b14f9 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -43,6 +43,7 @@ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ #define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ #define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ @@ -692,6 +693,7 @@ struct flash_info { #define M25P_NO_ERASE 0x02 /* No erase command needed */ #define SST_WRITE 0x04 /* use SST byte programming */ #define M25P_NO_FR 0x08 /* Can't do fastread */ +#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -773,6 +775,11 @@ static const struct spi_device_id m25p_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, + /* PMC */ + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, + { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, + { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, + /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ @@ -1026,6 +1033,9 @@ static int m25p_probe(struct spi_device *spi) if (info->flags & SECT_4K) { flash->erase_opcode = OPCODE_BE_4K; flash->mtd.erasesize = 4096; + } else if (info->flags & SECT_4K_PMC) { + flash->erase_opcode = OPCODE_BE_4K_PMC; + flash->mtd.erasesize = 4096; } else { flash->erase_opcode = OPCODE_SE; flash->mtd.erasesize = info->sector_size; -- cgit v0.10.2