From c9ec3900abf279d9276a0661c9bed2550c1f0bb5 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 13 Aug 2015 15:46:03 -0700 Subject: mtd: spi-nor: assign mtd->priv in spi_nor_scan() Layering suggests that the SPI NOR layer (not the hardware driver) should be initializing the MTD layer. Signed-off-by: Brian Norris Tested-by: Joachim Eastwood diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 9cd3631..24965ae 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -205,7 +205,6 @@ static int m25p_probe(struct spi_device *spi) nor->priv = flash; spi_set_drvdata(spi, flash); - flash->mtd.priv = nor; flash->spi = spi; if (spi->mode & SPI_RX_QUAD) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index d32b7e0..ad9f73a 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -1018,7 +1018,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) nor->mtd = mtd; nor->dev = dev; nor->priv = q; - mtd->priv = nor; /* fill the hooks */ nor->read_reg = fsl_qspi_read_reg; diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c index 9ad1dd0..ce6a478 100644 --- a/drivers/mtd/spi-nor/nxp-spifi.c +++ b/drivers/mtd/spi-nor/nxp-spifi.c @@ -331,7 +331,6 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, writel(ctrl, spifi->io_base + SPIFI_CTRL); - spifi->mtd.priv = &spifi->nor; spifi->nor.mtd = &spifi->mtd; spifi->nor.dev = spifi->dev; spifi->nor.priv = spifi; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 25372f9..d98180e 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1061,6 +1061,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) if (!mtd->name) mtd->name = dev_name(dev); + mtd->priv = nor; mtd->type = MTD_NORFLASH; mtd->writesize = 1; mtd->flags = MTD_CAP_NORFLASH; -- cgit v0.10.2 From a39f1d5e4303f535840a44eee87326fd9743de7c Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 13 Aug 2015 15:46:04 -0700 Subject: mtd: spi-nor: add forward declaration for mtd_info This header can't actually stand alone, as it relies on the declaration (but not definition) of struct mtd_info. Let's fix that. Signed-off-by: Brian Norris Tested-by: Joachim Eastwood diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index e540952..85a24ba 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -127,6 +127,8 @@ enum spi_nor_option_flags { SNOR_F_USE_FSR = BIT(0), }; +struct mtd_info; + /** * struct spi_nor - Structure for defining a the SPI NOR layer * @mtd: point to a mtd_info structure -- cgit v0.10.2 From 1976367173a47f801c67b5f456922d79c60d0d42 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 13 Aug 2015 15:46:05 -0700 Subject: mtd: spi-nor: embed struct mtd_info within struct spi_nor This reflects the proper layering, so let's do it. Signed-off-by: Brian Norris Tested-by: Joachim Eastwood diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 24965ae..b6bc9a2 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -31,7 +31,6 @@ struct m25p { struct spi_device *spi; struct spi_nor spi_nor; - struct mtd_info mtd; u8 command[MAX_CMD_SIZE]; }; @@ -159,7 +158,7 @@ static int m25p80_erase(struct spi_nor *nor, loff_t offset) struct m25p *flash = nor->priv; dev_dbg(nor->dev, "%dKiB at 0x%08x\n", - flash->mtd.erasesize / 1024, (u32)offset); + flash->spi_nor.mtd.erasesize / 1024, (u32)offset); /* Set up command buffer. */ flash->command[0] = nor->erase_opcode; @@ -201,7 +200,6 @@ static int m25p_probe(struct spi_device *spi) nor->read_reg = m25p80_read_reg; nor->dev = &spi->dev; - nor->mtd = &flash->mtd; nor->priv = flash; spi_set_drvdata(spi, flash); @@ -213,7 +211,7 @@ static int m25p_probe(struct spi_device *spi) mode = SPI_NOR_DUAL; if (data && data->name) - flash->mtd.name = data->name; + nor->mtd.name = data->name; /* For some (historical?) reason many platforms provide two different * names in flash_platform_data: "name" and "type". Quite often name is @@ -231,7 +229,7 @@ static int m25p_probe(struct spi_device *spi) ppdata.of_node = spi->dev.of_node; - return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, + return mtd_device_parse_register(&nor->mtd, NULL, &ppdata, data ? data->parts : NULL, data ? data->nr_parts : 0); } @@ -242,7 +240,7 @@ static int m25p_remove(struct spi_device *spi) struct m25p *flash = spi_get_drvdata(spi); /* Clean up MTD stuff. */ - return mtd_device_unregister(&flash->mtd); + return mtd_device_unregister(&flash->spi_nor.mtd); } /* diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index ad9f73a..19f6178 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -259,7 +259,6 @@ static struct fsl_qspi_devtype_data imx6ul_data = { #define FSL_QSPI_MAX_CHIP 4 struct fsl_qspi { - struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; struct spi_nor nor[FSL_QSPI_MAX_CHIP]; void __iomem *iobase; void __iomem *ahb_addr; @@ -888,7 +887,7 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs) int ret; dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n", - nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs); + nor->mtd.erasesize / 1024, q->chip_base_addr, (u32)offs); ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0); if (ret) @@ -1013,9 +1012,8 @@ static int fsl_qspi_probe(struct platform_device *pdev) i *= 2; nor = &q->nor[i]; - mtd = &q->mtd[i]; + mtd = &nor->mtd; - nor->mtd = mtd; nor->dev = dev; nor->priv = q; @@ -1086,7 +1084,7 @@ last_init_failed: /* skip the holes */ if (!q->has_second_chip) i *= 2; - mtd_device_unregister(&q->mtd[i]); + mtd_device_unregister(&q->nor[i].mtd); } mutex_failed: mutex_destroy(&q->lock); @@ -1106,7 +1104,7 @@ static int fsl_qspi_remove(struct platform_device *pdev) /* skip the holes */ if (!q->has_second_chip) i *= 2; - mtd_device_unregister(&q->mtd[i]); + mtd_device_unregister(&q->nor[i].mtd); } /* disable the hardware */ diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c index ce6a478..0f6b452 100644 --- a/drivers/mtd/spi-nor/nxp-spifi.c +++ b/drivers/mtd/spi-nor/nxp-spifi.c @@ -60,7 +60,6 @@ struct nxp_spifi { struct clk *clk_reg; void __iomem *io_base; void __iomem *flash_base; - struct mtd_info mtd; struct spi_nor nor; bool memory_mode; u32 mcmd; @@ -331,7 +330,6 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, writel(ctrl, spifi->io_base + SPIFI_CTRL); - spifi->nor.mtd = &spifi->mtd; spifi->nor.dev = spifi->dev; spifi->nor.priv = spifi; spifi->nor.read = nxp_spifi_read; @@ -364,7 +362,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, } ppdata.of_node = np; - ret = mtd_device_parse_register(&spifi->mtd, NULL, &ppdata, NULL, 0); + ret = mtd_device_parse_register(&spifi->nor.mtd, NULL, &ppdata, NULL, 0); if (ret) { dev_err(spifi->dev, "mtd device parse failed\n"); return ret; @@ -453,7 +451,7 @@ static int nxp_spifi_remove(struct platform_device *pdev) { struct nxp_spifi *spifi = platform_get_drvdata(pdev); - mtd_device_unregister(&spifi->mtd); + mtd_device_unregister(&spifi->nor.mtd); clk_disable_unprepare(spifi->clk_spifi); clk_disable_unprepare(spifi->clk_reg); diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index d98180e..834a060 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -265,7 +265,7 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor) */ static int erase_chip(struct spi_nor *nor) { - dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10)); + dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); } @@ -373,7 +373,7 @@ erase_err: static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) { - struct mtd_info *mtd = nor->mtd; + struct mtd_info *mtd = &nor->mtd; uint32_t offset = ofs; uint8_t status_old, status_new; int ret = 0; @@ -407,7 +407,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) { - struct mtd_info *mtd = nor->mtd; + struct mtd_info *mtd = &nor->mtd; uint32_t offset = ofs; uint8_t status_old, status_new; int ret = 0; @@ -1004,7 +1004,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) { const struct flash_info *info = NULL; struct device *dev = nor->dev; - struct mtd_info *mtd = nor->mtd; + struct mtd_info *mtd = &nor->mtd; struct device_node *np = dev->of_node; int ret; int i; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 85a24ba..495433d 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -162,7 +162,7 @@ struct mtd_info; * @priv: the private data */ struct spi_nor { - struct mtd_info *mtd; + struct mtd_info mtd; struct mutex lock; struct device *dev; u32 page_size; -- cgit v0.10.2 From e747dbe75e83345379455a78bb208ab7202229df Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 13 Aug 2015 15:46:02 -0700 Subject: mtd: fsl-quadspi: use automatic spi-nor detection We don't really need the flash information from the device tree here. Let's stick with autodetection here instead. Signed-off-by: Brian Norris Acked-by: Han Xu Tested-by: Han Xu diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 19f6178..f28dcc1 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -1005,8 +1005,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* iterate the subnodes. */ for_each_available_child_of_node(dev->of_node, np) { - char modalias[40]; - /* skip the holes */ if (!q->has_second_chip) i *= 2; @@ -1027,10 +1025,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) nor->prepare = fsl_qspi_prep; nor->unprepare = fsl_qspi_unprep; - ret = of_modalias_node(np, modalias, sizeof(modalias)); - if (ret < 0) - goto mutex_failed; - ret = of_property_read_u32(np, "spi-max-frequency", &q->clk_rate); if (ret < 0) @@ -1039,7 +1033,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* set the chip address for READID */ fsl_qspi_set_base_addr(q, nor); - ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD); + ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); if (ret) goto mutex_failed; -- cgit v0.10.2 From a9cadf72bfb7185a680eb7599b9bda65d1515b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ezequiel=20Garc=C3=ADa?= Date: Fri, 21 Aug 2015 15:47:28 -0300 Subject: mtd: pxa3xx_nand: Remove unused platform-data flash specification The driver supports board files specificating the flash device, by passing a pxa3xx_nand_flash struct (with flash parameters) in the platform data struct. Currently this support is not being used by any board file. Moreover, we'd like to deprecate such usage in favor of using the device table in nand_ids.c. So let's remove the ad-hoc flash specification. Signed-off-by: Ezequiel Garcia Acked-by: Robert Jarzmik Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 740983a..165112c 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -252,6 +252,30 @@ 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"); +struct pxa3xx_nand_timing { + unsigned int tCH; /* Enable signal hold time */ + unsigned int tCS; /* Enable signal setup time */ + unsigned int tWH; /* ND_nWE high duration */ + unsigned int tWP; /* ND_nWE pulse time */ + unsigned int tRH; /* ND_nRE high duration */ + unsigned int tRP; /* ND_nRE pulse width */ + unsigned int tR; /* ND_nWE high to ND_nRE low for read */ + unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ + unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ +}; + +struct pxa3xx_nand_flash { + char *name; + uint32_t chip_id; + unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */ + unsigned int page_size; /* Page size in bytes (PAGE_SZ) */ + unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ + unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ + unsigned int num_blocks; /* Number of physical blocks in Flash */ + + struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ +}; + static struct pxa3xx_nand_timing timing[] = { { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, @@ -1491,19 +1515,16 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) return -EINVAL; } - num = ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; + num = ARRAY_SIZE(builtin_flash_types) - 1; for (i = 0; i < num; i++) { - if (i < pdata->num_flash) - f = pdata->flash + i; - else - f = &builtin_flash_types[i - pdata->num_flash + 1]; + f = &builtin_flash_types[i + 1]; /* find the chip in default list */ if (f->chip_id == id) break; } - if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) { + if (i >= (ARRAY_SIZE(builtin_flash_types) - 1)) { dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n"); return -EINVAL; diff --git a/include/linux/platform_data/mtd-nand-pxa3xx.h b/include/linux/platform_data/mtd-nand-pxa3xx.h index ac4ea2e..394d155 100644 --- a/include/linux/platform_data/mtd-nand-pxa3xx.h +++ b/include/linux/platform_data/mtd-nand-pxa3xx.h @@ -4,30 +4,6 @@ #include #include -struct pxa3xx_nand_timing { - unsigned int tCH; /* Enable signal hold time */ - unsigned int tCS; /* Enable signal setup time */ - unsigned int tWH; /* ND_nWE high duration */ - unsigned int tWP; /* ND_nWE pulse time */ - unsigned int tRH; /* ND_nRE high duration */ - unsigned int tRP; /* ND_nRE pulse width */ - unsigned int tR; /* ND_nWE high to ND_nRE low for read */ - unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ - unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ -}; - -struct pxa3xx_nand_flash { - char *name; - uint32_t chip_id; - unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */ - unsigned int page_size; /* Page size in bytes (PAGE_SZ) */ - unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ - unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ - unsigned int num_blocks; /* Number of physical blocks in Flash */ - - struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ -}; - /* * Current pxa3xx_nand controller has two chip select which * both be workable. @@ -63,9 +39,6 @@ struct pxa3xx_nand_platform_data { const struct mtd_partition *parts[NUM_CHIP_SELECT]; unsigned int nr_parts[NUM_CHIP_SELECT]; - - const struct pxa3xx_nand_flash * flash; - size_t num_flash; }; extern void pxa3xx_set_nand_info(struct pxa3xx_nand_platform_data *info); -- cgit v0.10.2 From 7eadd47fd708df2028877558d085f47dc9086459 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 28 Aug 2015 14:45:21 +0200 Subject: mtd: sunxi_nand: Select the chip in sunxi_nand_chip_init_timings() nand_scan_ident() leaves the chip deselected. So just issuing some commands from the sunxi driver does not work. We need to select the chip before writing the commands to the NAND device. This patch takes care of this. Set the new timing on all dies implemented as suggested by Boris. This was detected on the in-circuit ICnova-A20 SoM equipped with the Micron MT29F32G08CBACAWP (4GiB) ONFI NAND device. Signed-off-by: Stefan Roese Cc: Hans de Goede Cc: Maxime Ripard Cc: Roy Spliet Acked-by: Boris Brezillon [Brian: fixup whitespace] Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index f97a58d..4e586bc 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -978,17 +978,23 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip, mode = chip->nand.onfi_timing_mode_default; } else { uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {}; + int i; mode = fls(mode) - 1; if (mode < 0) mode = 0; feature[0] = mode; - ret = chip->nand.onfi_set_features(&chip->mtd, &chip->nand, + for (i = 0; i < chip->nsels; i++) { + chip->nand.select_chip(&chip->mtd, i); + ret = chip->nand.onfi_set_features(&chip->mtd, + &chip->nand, ONFI_FEATURE_ADDR_TIMING_MODE, feature); - if (ret) - return ret; + chip->nand.select_chip(&chip->mtd, -1); + if (ret) + return ret; + } } timings = onfi_async_timing_mode_to_sdr_timings(mode); -- cgit v0.10.2 From 11bff0b70cc003b995cf4c2acec4adab90957b02 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 3 Sep 2015 18:35:36 +0200 Subject: mtd: spi-nor: Decouple SPI NOR's device_node from controller device The problem this patch is trying to address is such, that SPI NOR flash devices attached to a dedicated SPI NOR controller cannot read their properties from the associated struct device_node. A couple of facts first: 1) Each SPI NOR flash has a struct spi_nor associated with it. 2) Each SPI NOR flash has certain device properties associated with it, for example the OF property 'm25p,fast-read' is a good pick. These properties are used by the SPI NOR core to select which opcodes are sent to such SPI NOR flash. These properties are coming from spi_nor .dev->of_node . The problem is, that for SPI NOR controllers, the struct spi_nor .dev element points to the struct device of the SPI NOR controller, not the SPI NOR flash. Therefore, the associated dev->of_node also is the one of the controller and therefore the SPI NOR core code is trying to parse the SPI NOR controller's properties, not the properties of the SPI NOR flash. Note: The m25p80 driver is not affected, because the controller and the flash are the same device, so the associated device_node of the controller and the flash are the same. This patch adjusts the SPI NOR core such that the device_node is not picked from spi_nor .dev directly, but from a new separate spi_nor .flash_node element. This let's the SPI NOR controller drivers set up a different spi_nor .flash_node element for each SPI NOR flash. This patch also fixes the controller drivers to be compatible with this modification and correctly set the spi_nor .flash_node element. This patch is inspired by 5844feeaa4154d1c46d3462c7a4653d22356d8b4 mtd: nand: add common DT init code Signed-off-by: Marek Vasut Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index b6bc9a2..05bf0d7 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -200,6 +200,7 @@ static int m25p_probe(struct spi_device *spi) nor->read_reg = m25p80_read_reg; nor->dev = &spi->dev; + nor->flash_node = spi->dev.of_node; nor->priv = flash; spi_set_drvdata(spi, flash); diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index f28dcc1..f724832 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -1013,6 +1013,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) mtd = &nor->mtd; nor->dev = dev; + nor->flash_node = np; nor->priv = q; /* fill the hooks */ diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c index 0f6b452..ce7bf6b 100644 --- a/drivers/mtd/spi-nor/nxp-spifi.c +++ b/drivers/mtd/spi-nor/nxp-spifi.c @@ -331,6 +331,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, writel(ctrl, spifi->io_base + SPIFI_CTRL); spifi->nor.dev = spifi->dev; + spifi->nor.flash_node = np; spifi->nor.priv = spifi; spifi->nor.read = nxp_spifi_read; spifi->nor.write = nxp_spifi_write; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 55c67a4..22cca87 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1005,7 +1005,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) const struct flash_info *info = NULL; struct device *dev = nor->dev; struct mtd_info *mtd = &nor->mtd; - struct device_node *np = dev->of_node; + struct device_node *np = nor->flash_node; int ret; int i; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 495433d..6379e10 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -134,6 +134,7 @@ struct mtd_info; * @mtd: point to a mtd_info structure * @lock: the lock for the read/write/erase/lock/unlock operations * @dev: point to a spi device, or a spi nor controller device. + * @flash_node: point to a device node describing this flash instance. * @page_size: the page size of the SPI NOR * @addr_width: number of address bytes * @erase_opcode: the opcode for erasing a sector @@ -165,6 +166,7 @@ struct spi_nor { struct mtd_info mtd; struct mutex lock; struct device *dev; + struct device_node *flash_node; u32 page_size; u8 addr_width; u8 erase_opcode; -- cgit v0.10.2 From 61528d888adf9470db73aaadf1ff9ca35942262a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 3 Sep 2015 18:35:37 +0200 Subject: mtd: nand: Rename nand_chip .dn to .flash_node Use a more descriptive name for the device_node element in struct nand_chip . This name matches the element name used for device_node property of a flash in the spi-nor framework. Signed-off-by: Marek Vasut Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index fddb795..048e4e0 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1792,7 +1792,8 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) memset(cfg, 0, sizeof(*cfg)); - ret = of_property_read_u32(chip->dn, "brcm,nand-oob-sector-size", + ret = of_property_read_u32(chip->flash_node, + "brcm,nand-oob-sector-size", &oob_sector); if (ret) { /* Use detected size */ @@ -1899,7 +1900,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host) mtd = &host->mtd; chip = &host->chip; - chip->dn = dn; + chip->flash_node = dn; chip->priv = host; mtd->priv = chip; mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d", diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ceb68ca..51f7816 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3846,8 +3846,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, struct nand_flash_dev *type; int ret; - if (chip->dn) { - ret = nand_dt_init(mtd, chip, chip->dn); + if (chip->flash_node) { + ret = nand_dt_init(mtd, chip, chip->flash_node); if (ret) return ret; } diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 272f429..3140b3d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -544,7 +544,7 @@ struct nand_buffers { * flash device * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the * flash device. - * @dn: [BOARDSPECIFIC] device node describing this instance + * @flash_node: [BOARDSPECIFIC] device node describing this instance * @read_byte: [REPLACEABLE] read one byte from the chip * @read_word: [REPLACEABLE] read one word from the chip * @write_byte: [REPLACEABLE] write a single byte to the chip on the @@ -647,7 +647,7 @@ struct nand_chip { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; - struct device_node *dn; + struct device_node *flash_node; uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); -- cgit v0.10.2 From a3d22a55cda99bcd151836f39ddd329af6cb1d30 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 2 Sep 2015 10:30:25 +0200 Subject: mtd: nand: sunxi: rely on nand_dt_init initialization nand_dt_init(), called from nand_scan_ident(), is already parsing the generic MTD/NAND DT properties, and initializing the nand_chip struct accordingly. Rely on this initialization instead of manually parsing those properties. Signed-off-by: Boris Brezillon [Brian: rename 'np' -> 'flash_node' to accomodate for prior rename patch] Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 4e586bc..4f9db77 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1168,16 +1168,9 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, struct device_node *np) { struct nand_chip *nand = mtd->priv; - int strength; - int blk_size; int ret; - blk_size = of_get_nand_ecc_step_size(np); - strength = of_get_nand_ecc_strength(np); - if (blk_size > 0 && strength > 0) { - ecc->size = blk_size; - ecc->strength = strength; - } else { + if (!ecc->size) { ecc->size = nand->ecc_step_ds; ecc->strength = nand->ecc_strength_ds; } @@ -1185,12 +1178,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, if (!ecc->size || !ecc->strength) return -EINVAL; - ecc->mode = NAND_ECC_HW; - - ret = of_get_nand_ecc_mode(np); - if (ret >= 0) - ecc->mode = ret; - switch (ecc->mode) { case NAND_ECC_SOFT_BCH: break; @@ -1316,15 +1303,18 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ nand->chip_delay = 200; nand->controller = &nfc->controller; + /* + * Set the ECC mode to the default value in case nothing is specified + * in the DT. + */ + nand->ecc.mode = NAND_ECC_HW; + nand->flash_node = np; nand->select_chip = sunxi_nfc_select_chip; nand->cmd_ctrl = sunxi_nfc_cmd_ctrl; nand->read_buf = sunxi_nfc_read_buf; nand->write_buf = sunxi_nfc_write_buf; nand->read_byte = sunxi_nfc_read_byte; - if (of_get_nand_on_flash_bbt(np)) - nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; - mtd = &chip->mtd; mtd->dev.parent = dev; mtd->priv = nand; @@ -1334,6 +1324,9 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, if (ret) return ret; + if (nand->bbt_options & NAND_BBT_USE_FLASH) + nand->bbt_options |= NAND_BBT_NO_OOB; + ret = sunxi_nand_chip_init_timings(chip, np); if (ret) { dev_err(dev, "could not configure chip timings: %d\n", ret); -- cgit v0.10.2 From fd7252346b2b005ea5c5f1b432a63dbb9bb05768 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Wed, 19 Aug 2015 15:26:43 +0530 Subject: mtd: spi-nor: Use write_sr for write status Use existing write_sr() call instead of decoding and calling nor->write_reg separately. Signed-off-by: Jagan Teki Cc: David Woodhouse Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 22cca87..3694f66 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -868,8 +868,7 @@ static int macronix_quad_enable(struct spi_nor *nor) val = read_sr(nor); write_enable(nor); - nor->cmd_buf[0] = val | SR_QUAD_EN_MX; - nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); + write_sr(nor, val | SR_QUAD_EN_MX); if (spi_nor_wait_till_ready(nor)) return 1; -- cgit v0.10.2 From f9f3ce835ddce3c669eee869253105f88819888b Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Wed, 19 Aug 2015 15:26:44 +0530 Subject: mtd: spi-nor: Zap unneeded write_enable from write_reg The 'write_enable' argument is unused and unneeded, so remove it from the API. Signed-off-by: Jagan Teki Cc: David Woodhouse Cc: Han Xu [Brian: fixed for nxp-spifi.c] Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 05bf0d7..4b5d7a4 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -61,8 +61,7 @@ static int m25p_cmdsz(struct spi_nor *nor) return 1 + nor->addr_width; } -static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, - int wr_en) +static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) { struct m25p *flash = nor->priv; struct spi_device *spi = flash->spi; diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index f724832..2954f89 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -797,8 +797,7 @@ static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) return 0; } -static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, - int write_enable) +static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) { struct fsl_qspi *q = nor->priv; int ret; diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c index ce7bf6b..9e82098 100644 --- a/drivers/mtd/spi-nor/nxp-spifi.c +++ b/drivers/mtd/spi-nor/nxp-spifi.c @@ -149,8 +149,7 @@ static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) return nxp_spifi_wait_for_cmd(spifi); } -static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, - int len, int write_enable) +static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) { struct nxp_spifi *spifi = nor->priv; u32 cmd; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 3694f66..8818d43 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -145,7 +145,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) static inline int write_sr(struct spi_nor *nor, u8 val) { nor->cmd_buf[0] = val; - return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1); } /* @@ -154,7 +154,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val) */ static inline int write_enable(struct spi_nor *nor) { - return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); } /* @@ -162,7 +162,7 @@ static inline int write_enable(struct spi_nor *nor) */ static inline int write_disable(struct spi_nor *nor) { - return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0); } static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) @@ -188,7 +188,7 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, write_enable(nor); cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; - status = nor->write_reg(nor, cmd, NULL, 0, 0); + status = nor->write_reg(nor, cmd, NULL, 0); if (need_wren) write_disable(nor); @@ -196,7 +196,7 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, default: /* Spansion style */ nor->cmd_buf[0] = enable << 7; - return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); + return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); } } static inline int spi_nor_sr_ready(struct spi_nor *nor) @@ -267,7 +267,7 @@ static int erase_chip(struct spi_nor *nor) { dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); - return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0); } static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) @@ -893,7 +893,7 @@ static int write_sr_cr(struct spi_nor *nor, u16 val) nor->cmd_buf[0] = val & 0xff; nor->cmd_buf[1] = (val >> 8); - return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0); + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2); } static int spansion_quad_enable(struct spi_nor *nor) @@ -935,7 +935,7 @@ static int micron_quad_enable(struct spi_nor *nor) /* set EVCR, enable quad I/O */ nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON; - ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1, 0); + ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1); if (ret < 0) { dev_err(nor->dev, "error while writing EVCR register\n"); return ret; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 6379e10..e9c912d 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -186,8 +186,7 @@ struct spi_nor { int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, u8 *buf, size_t len); int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); - int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, - int write_enable); + int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); int (*read)(struct spi_nor *nor, loff_t from, size_t len, size_t *retlen, u_char *read_buf); -- cgit v0.10.2 From 730a43fbc135e593cc3de3b1b895e49c05c8e2dc Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 3 Sep 2015 18:03:38 +0200 Subject: mtd: nand: add nand_check_erased helper functions Add two helper functions to help NAND controller drivers test whether a specific NAND region is erased or not. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 51f7816..37c0d9d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1101,6 +1101,134 @@ out: EXPORT_SYMBOL(nand_lock); /** + * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data + * @buf: buffer to test + * @len: buffer length + * @bitflips_threshold: maximum number of bitflips + * + * Check if a buffer contains only 0xff, which means the underlying region + * has been erased and is ready to be programmed. + * The bitflips_threshold specify the maximum number of bitflips before + * considering the region is not erased. + * Note: The logic of this function has been extracted from the memweight + * implementation, except that nand_check_erased_buf function exit before + * testing the whole buffer if the number of bitflips exceed the + * bitflips_threshold value. + * + * Returns a positive number of bitflips less than or equal to + * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the + * threshold. + */ +static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold) +{ + const unsigned char *bitmap = buf; + int bitflips = 0; + int weight; + + for (; len && ((uintptr_t)bitmap) % sizeof(long); + len--, bitmap++) { + weight = hweight8(*bitmap); + bitflips += BITS_PER_BYTE - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + for (; len >= sizeof(long); + len -= sizeof(long), bitmap += sizeof(long)) { + weight = hweight_long(*((unsigned long *)bitmap)); + bitflips += BITS_PER_LONG - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + for (; len > 0; len--, bitmap++) { + weight = hweight8(*bitmap); + bitflips += BITS_PER_BYTE - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + return bitflips; +} + +/** + * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only + * 0xff data + * @data: data buffer to test + * @datalen: data length + * @ecc: ECC buffer + * @ecclen: ECC length + * @extraoob: extra OOB buffer + * @extraooblen: extra OOB length + * @bitflips_threshold: maximum number of bitflips + * + * Check if a data buffer and its associated ECC and OOB data contains only + * 0xff pattern, which means the underlying region has been erased and is + * ready to be programmed. + * The bitflips_threshold specify the maximum number of bitflips before + * considering the region as not erased. + * + * Note: + * 1/ ECC algorithms are working on pre-defined block sizes which are usually + * different from the NAND page size. When fixing bitflips, ECC engines will + * report the number of errors per chunk, and the NAND core infrastructure + * expect you to return the maximum number of bitflips for the whole page. + * This is why you should always use this function on a single chunk and + * not on the whole page. After checking each chunk you should update your + * max_bitflips value accordingly. + * 2/ When checking for bitflips in erased pages you should not only check + * the payload data but also their associated ECC data, because a user might + * have programmed almost all bits to 1 but a few. In this case, we + * shouldn't consider the chunk as erased, and checking ECC bytes prevent + * this case. + * 3/ The extraoob argument is optional, and should be used if some of your OOB + * data are protected by the ECC engine. + * It could also be used if you support subpages and want to attach some + * extra OOB data to an ECC chunk. + * + * Returns a positive number of bitflips less than or equal to + * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the + * threshold. In case of success, the passed buffers are filled with 0xff. + */ +int nand_check_erased_ecc_chunk(void *data, int datalen, + void *ecc, int ecclen, + void *extraoob, int extraooblen, + int bitflips_threshold) +{ + int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0; + + data_bitflips = nand_check_erased_buf(data, datalen, + bitflips_threshold); + if (data_bitflips < 0) + return data_bitflips; + + bitflips_threshold -= data_bitflips; + + ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold); + if (ecc_bitflips < 0) + return ecc_bitflips; + + bitflips_threshold -= ecc_bitflips; + + extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen, + bitflips_threshold); + if (extraoob_bitflips < 0) + return extraoob_bitflips; + + if (data_bitflips) + memset(data, 0xff, datalen); + + if (ecc_bitflips) + memset(ecc, 0xff, ecclen); + + if (extraoob_bitflips) + memset(extraoob, 0xff, extraooblen); + + return data_bitflips + ecc_bitflips + extraoob_bitflips; +} +EXPORT_SYMBOL(nand_check_erased_ecc_chunk); + +/** * nand_read_page_raw - [INTERN] read raw page data without ecc * @mtd: mtd info structure * @chip: nand chip info structure diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 3140b3d..c4d8e30 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -1030,4 +1030,9 @@ struct nand_sdr_timings { /* get timing characteristics from ONFI timing mode. */ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode); + +int nand_check_erased_ecc_chunk(void *data, int datalen, + void *ecc, int ecclen, + void *extraoob, int extraooblen, + int threshold); #endif /* __LINUX_MTD_NAND_H */ -- cgit v0.10.2 From 79c452adb159dc9abc507ea13faec8d115a78758 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Fri, 18 Sep 2015 17:49:25 +0200 Subject: mtd: spi-nor: remove unused read_xfer/write_xfer hooks struct spi_nor_xfer_cfg and read_xfer/write_xfer hooks were never used by any driver. Do some cleanup by removing them. Signed-off-by: Cyrille Pitchen Reviewed-by: Marek Vasut Signed-off-by: Brian Norris diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index e9c912d..672595a 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -87,33 +87,6 @@ enum read_mode { SPI_NOR_QUAD, }; -/** - * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer - * @wren: command for "Write Enable", or 0x00 for not required - * @cmd: command for operation - * @cmd_pins: number of pins to send @cmd (1, 2, 4) - * @addr: address for operation - * @addr_pins: number of pins to send @addr (1, 2, 4) - * @addr_width: number of address bytes - * (3,4, or 0 for address not required) - * @mode: mode data - * @mode_pins: number of pins to send @mode (1, 2, 4) - * @mode_cycles: number of mode cycles (0 for mode not required) - * @dummy_cycles: number of dummy cycles (0 for dummy not required) - */ -struct spi_nor_xfer_cfg { - u8 wren; - u8 cmd; - u8 cmd_pins; - u32 addr; - u8 addr_pins; - u8 addr_width; - u8 mode; - u8 mode_pins; - u8 mode_cycles; - u8 dummy_cycles; -}; - #define SPI_NOR_MAX_CMD_SIZE 8 enum spi_nor_ops { SPI_NOR_OPS_READ = 0, @@ -144,14 +117,11 @@ struct mtd_info; * @flash_read: the mode of the read * @sst_write_second: used by the SST write operation * @flags: flag options for the current SPI-NOR (SNOR_F_*) - * @cfg: used by the read_xfer/write_xfer * @cmd_buf: used by the write_reg * @prepare: [OPTIONAL] do some preparations for the * read/write/erase/lock/unlock operations * @unprepare: [OPTIONAL] do some post work after the * read/write/erase/lock/unlock operations - * @read_xfer: [OPTIONAL] the read fundamental primitive - * @write_xfer: [OPTIONAL] the writefundamental primitive * @read_reg: [DRIVER-SPECIFIC] read out the register * @write_reg: [DRIVER-SPECIFIC] write data to the register * @read: [DRIVER-SPECIFIC] read data from the SPI NOR @@ -176,15 +146,10 @@ struct spi_nor { enum read_mode flash_read; bool sst_write_second; u32 flags; - struct spi_nor_xfer_cfg cfg; u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); - int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, - u8 *buf, size_t len); - int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, - u8 *buf, size_t len); int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); -- cgit v0.10.2 From 0791a5f8ab5082d2726d99f3b21036d438c11424 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 13 Sep 2015 14:14:54 +0200 Subject: mtd: nandsim: drop null test before destroy functions Remove unneeded NULL test. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression x; @@ -if (x != NULL) \(kmem_cache_destroy\|mempool_destroy\|dma_pool_destroy\)(x); // Signed-off-by: Julia Lawall Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 95d0cc4..b16d70a 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -649,8 +649,7 @@ static void free_device(struct nandsim *ns) kmem_cache_free(ns->nand_pages_slab, ns->pages[i].byte); } - if (ns->nand_pages_slab) - kmem_cache_destroy(ns->nand_pages_slab); + kmem_cache_destroy(ns->nand_pages_slab); vfree(ns->pages); } } -- cgit v0.10.2 From e1305df1283cbe1aa57093f8766b2dfe650ed5ff Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 13 Sep 2015 14:15:29 +0200 Subject: jffs2: drop null test before destroy functions Remove unneeded NULL test. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression x; @@ -if (x != NULL) \(kmem_cache_destroy\|mempool_destroy\|dma_pool_destroy\)(x); // Signed-off-by: Julia Lawall Signed-off-by: Brian Norris diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index b8fd651..ce11897 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -97,25 +97,16 @@ int __init jffs2_create_slab_caches(void) void jffs2_destroy_slab_caches(void) { - if(full_dnode_slab) - kmem_cache_destroy(full_dnode_slab); - if(raw_dirent_slab) - kmem_cache_destroy(raw_dirent_slab); - if(raw_inode_slab) - kmem_cache_destroy(raw_inode_slab); - if(tmp_dnode_info_slab) - kmem_cache_destroy(tmp_dnode_info_slab); - if(raw_node_ref_slab) - kmem_cache_destroy(raw_node_ref_slab); - if(node_frag_slab) - kmem_cache_destroy(node_frag_slab); - if(inode_cache_slab) - kmem_cache_destroy(inode_cache_slab); + kmem_cache_destroy(full_dnode_slab); + kmem_cache_destroy(raw_dirent_slab); + kmem_cache_destroy(raw_inode_slab); + kmem_cache_destroy(tmp_dnode_info_slab); + kmem_cache_destroy(raw_node_ref_slab); + kmem_cache_destroy(node_frag_slab); + kmem_cache_destroy(inode_cache_slab); #ifdef CONFIG_JFFS2_FS_XATTR - if (xattr_datum_cache) - kmem_cache_destroy(xattr_datum_cache); - if (xattr_ref_cache) - kmem_cache_destroy(xattr_ref_cache); + kmem_cache_destroy(xattr_datum_cache); + kmem_cache_destroy(xattr_ref_cache); #endif } -- cgit v0.10.2 From 8f5ba31aa565c4efe11cca2b37acbb35c4f143f2 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 6 Sep 2015 15:12:47 +0200 Subject: mtd: nand: pxa3xx-nand: switch to dmaengine Now pxa architecture has a dmaengine driver, remove the access to direct dma registers in favor of the more generic dmaengine code. This should be also applicable for mmp and orion, provided they work in device-tree environment. This patch also removes the previous hack which was necessary to make the driver work in a devicetree environment. Signed-off-by: Robert Jarzmik Reviewed-by: Ezequiel Garcia Tested-by: Ezequiel Garcia [Brian: fixup use of 'enum dma_transfer_direction'] Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 165112c..da563cd 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -33,10 +35,6 @@ #define ARCH_HAS_DMA #endif -#ifdef ARCH_HAS_DMA -#include -#endif - #include #define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200) @@ -201,6 +199,10 @@ struct pxa3xx_nand_info { unsigned int oob_buff_pos; /* DMA information */ + struct scatterlist sg; + enum dma_data_direction dma_dir; + struct dma_chan *dma_chan; + dma_cookie_t dma_cookie; int drcmr_dat; int drcmr_cmd; @@ -208,8 +210,6 @@ struct pxa3xx_nand_info { unsigned char *oob_buff; dma_addr_t data_buff_phys; int data_dma_ch; - struct pxa_dma_desc *data_desc; - dma_addr_t data_desc_addr; struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; unsigned int state; @@ -492,6 +492,9 @@ static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) ndcr &= ~NDCR_ND_RUN; nand_writel(info, NDCR, ndcr); } + if (info->dma_chan) + dmaengine_terminate_all(info->dma_chan); + /* clear status bits */ nand_writel(info, NDSR, NDSR_MASK); } @@ -583,57 +586,61 @@ static void handle_data_pio(struct pxa3xx_nand_info *info) info->data_size -= do_bytes; } -#ifdef ARCH_HAS_DMA -static void start_data_dma(struct pxa3xx_nand_info *info) +static void pxa3xx_nand_data_dma_irq(void *data) { - struct pxa_dma_desc *desc = info->data_desc; - int dma_len = ALIGN(info->data_size + info->oob_size, 32); + struct pxa3xx_nand_info *info = data; + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(info->dma_chan, info->dma_cookie, &state); + if (likely(status == DMA_COMPLETE)) { + info->state = STATE_DMA_DONE; + } else { + dev_err(&info->pdev->dev, "DMA error on data channel\n"); + info->retcode = ERR_DMABUSERR; + } + dma_unmap_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); - desc->ddadr = DDADR_STOP; - desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len; + nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); + enable_int(info, NDCR_INT_MASK); +} + +static void start_data_dma(struct pxa3xx_nand_info *info) +{ + enum dma_transfer_direction direction; + struct dma_async_tx_descriptor *tx; switch (info->state) { case STATE_DMA_WRITING: - desc->dsadr = info->data_buff_phys; - desc->dtadr = info->mmio_phys + NDDB; - desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG; + info->dma_dir = DMA_TO_DEVICE; + direction = DMA_MEM_TO_DEV; break; case STATE_DMA_READING: - desc->dtadr = info->data_buff_phys; - desc->dsadr = info->mmio_phys + NDDB; - desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC; + info->dma_dir = DMA_FROM_DEVICE; + direction = DMA_DEV_TO_MEM; break; default: dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, info->state); BUG(); } - - DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch; - DDADR(info->data_dma_ch) = info->data_desc_addr; - DCSR(info->data_dma_ch) |= DCSR_RUN; -} - -static void pxa3xx_nand_data_dma_irq(int channel, void *data) -{ - struct pxa3xx_nand_info *info = data; - uint32_t dcsr; - - dcsr = DCSR(channel); - DCSR(channel) = dcsr; - - if (dcsr & DCSR_BUSERR) { - info->retcode = ERR_DMABUSERR; + info->sg.length = info->data_size + + (info->oob_size ? info->spare_size + info->ecc_size : 0); + dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); + + tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(&info->pdev->dev, "prep_slave_sg() failed\n"); + return; } - - info->state = STATE_DMA_DONE; - enable_int(info, NDCR_INT_MASK); - nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); + tx->callback = pxa3xx_nand_data_dma_irq; + tx->callback_param = info; + info->dma_cookie = dmaengine_submit(tx); + dma_async_issue_pending(info->dma_chan); + dev_dbg(&info->pdev->dev, "%s(dir=%d cookie=%x size=%u)\n", + __func__, direction, info->dma_cookie, info->sg.length); } -#else -static void start_data_dma(struct pxa3xx_nand_info *info) -{} -#endif static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data) { @@ -1319,36 +1326,50 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) return 0; } -#ifdef ARCH_HAS_DMA static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) { struct platform_device *pdev = info->pdev; - int data_desc_offset = info->buf_size - sizeof(struct pxa_dma_desc); + struct dma_slave_config config; + dma_cap_mask_t mask; + struct pxad_param param; + int ret; - if (use_dma == 0) { - info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); - if (info->data_buff == NULL) - return -ENOMEM; + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) + return -ENOMEM; + if (use_dma == 0) return 0; - } - info->data_buff = dma_alloc_coherent(&pdev->dev, info->buf_size, - &info->data_buff_phys, GFP_KERNEL); - if (info->data_buff == NULL) { - dev_err(&pdev->dev, "failed to allocate dma buffer\n"); - return -ENOMEM; - } + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; - info->data_desc = (void *)info->data_buff + data_desc_offset; - info->data_desc_addr = info->data_buff_phys + data_desc_offset; + sg_init_one(&info->sg, info->data_buff, info->buf_size); + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + param.prio = PXAD_PRIO_LOWEST; + param.drcmr = info->drcmr_dat; + info->dma_chan = dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m, &pdev->dev, + "data"); + if (!info->dma_chan) { + dev_err(&pdev->dev, "unable to request data dma channel\n"); + return -ENODEV; + } - info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW, - pxa3xx_nand_data_dma_irq, info); - if (info->data_dma_ch < 0) { - dev_err(&pdev->dev, "failed to request data dma\n"); - dma_free_coherent(&pdev->dev, info->buf_size, - info->data_buff, info->data_buff_phys); - return info->data_dma_ch; + memset(&config, 0, sizeof(config)); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.src_addr = info->mmio_phys + NDDB; + config.dst_addr = info->mmio_phys + NDDB; + config.src_maxburst = 32; + config.dst_maxburst = 32; + ret = dmaengine_slave_config(info->dma_chan, &config); + if (ret < 0) { + dev_err(&info->pdev->dev, + "dma channel configuration failed: %d\n", + ret); + return ret; } /* @@ -1361,29 +1382,12 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) { - struct platform_device *pdev = info->pdev; if (info->use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_coherent(&pdev->dev, info->buf_size, - info->data_buff, info->data_buff_phys); - } else { - kfree(info->data_buff); + dmaengine_terminate_all(info->dma_chan); + dma_release_channel(info->dma_chan); } -} -#else -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) -{ - info->data_buff = kmalloc(info->buf_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) { @@ -1683,34 +1687,23 @@ static int alloc_nand_resource(struct platform_device *pdev) return ret; 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; + 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; } irq = platform_get_irq(pdev, 0); @@ -1821,15 +1814,16 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) struct pxa3xx_nand_platform_data *pdata; struct mtd_part_parser_data ppdata = {}; struct pxa3xx_nand_info *info; - int ret, cs, probe_success; + int ret, cs, probe_success, dma_available; -#ifndef ARCH_HAS_DMA - if (use_dma) { + dma_available = IS_ENABLED(CONFIG_ARM) && + (IS_ENABLED(CONFIG_ARCH_PXA) || IS_ENABLED(CONFIG_ARCH_MMP)); + if (use_dma && !dma_available) { 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 1bc81eeeba99ed20b570423c311826811e162e6d Mon Sep 17 00:00:00 2001 From: Yaowei Bai Date: Mon, 28 Sep 2015 22:15:38 +0800 Subject: jffs2: remove unnecessary new_valid_dev check As new_valid_dev always returns 1, so !new_valid_dev check is not needed, remove it. Signed-off-by: Yaowei Bai Signed-off-by: Brian Norris diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 8118002..d211b8e 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -621,9 +621,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode uint32_t alloclen; int ret; - if (!new_valid_dev(rdev)) - return -EINVAL; - ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; -- cgit v0.10.2 From 98d1a5eea378a624e0b0795f63900189a693d938 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 18 Sep 2015 00:14:02 +0200 Subject: mtd: orion_nand: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Acked-by: Andrew Lunn Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index c3c6d30..64785f4 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -201,6 +201,7 @@ static const struct of_device_id orion_nand_of_match_table[] = { { .compatible = "marvell,orion-nand", }, {}, }; +MODULE_DEVICE_TABLE(of, orion_nand_of_match_table); #endif static struct platform_driver orion_nand_driver = { -- cgit v0.10.2 From 030a70b9af4ad6bb1fd715205efc44c576a9a0c4 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 18 Sep 2015 00:11:59 +0200 Subject: mtd: fsl_elbc_nand: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 04b22fd..b812074 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -946,6 +946,7 @@ static const struct of_device_id fsl_elbc_nand_match[] = { { .compatible = "fsl,elbc-fcm-nand", }, {} }; +MODULE_DEVICE_TABLE(of, fsl_elbc_nand_match); static struct platform_driver fsl_elbc_nand_driver = { .driver = { -- cgit v0.10.2 From 3f7f7a5f3fba43d3fbfef4d8406252104d1c684f Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 18 Sep 2015 00:12:30 +0200 Subject: mtd: fsl_ifc_nand: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index a4e27e8..429142e 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -1163,6 +1163,7 @@ static const struct of_device_id fsl_ifc_nand_match[] = { }, {} }; +MODULE_DEVICE_TABLE(of, fsl_ifc_nand_match); static struct platform_driver fsl_ifc_nand_driver = { .driver = { -- cgit v0.10.2 From 7446076e818814d3237b20a96112b44bdb3f8576 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 18 Sep 2015 00:13:01 +0200 Subject: mtd: mpc5121_nfc: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 2a49b53..019fe0d 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -841,6 +841,7 @@ static const struct of_device_id mpc5121_nfc_match[] = { { .compatible = "fsl,mpc5121-nfc", }, {}, }; +MODULE_DEVICE_TABLE(of, mpc5121_nfc_match); static struct platform_driver mpc5121_nfc_driver = { .probe = mpc5121_nfc_probe, -- cgit v0.10.2 From 2ce401d56b36a7492b26646b94d1fe9194880a1a Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 11 Sep 2015 21:41:47 +0800 Subject: mtd: blktrans: fix multiplication overflow In drivers/mtd/mtd_blkdevs.c: 406 set_capacity(gd, (new->size * tr->blksize) >> 9); The type of new->size is unsigned long and the type of tr->blksize is int, the result of 'new->size * tr->blksize' may exceed ULONG_MAX on 32bit machines. I use nand chip MT29F32G08CBADBWP which is 4GB and the parameters passed to kernel is 'mtdparts=gpmi-nand:-(user)', the whole nand chip will be treated as a 4GB mtd partition. new->size is 0x800000 and tr->blksize is 0x200, 'new->size * tr->blksize' however is 0. This is what we do not want to see. Using type cast u64 to fix the multiplication overflow issue. Signed-off-by: Peng Fan Cc: David Woodhouse Signed-off-by: Brian Norris diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 44dc965..cb47d79 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -399,7 +399,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) snprintf(gd->disk_name, sizeof(gd->disk_name), "%s%d", tr->name, new->devnum); - set_capacity(gd, (new->size * tr->blksize) >> 9); + set_capacity(gd, ((u64)new->size * tr->blksize) >> 9); /* Create the request queue */ spin_lock_init(&new->queue_lock); -- cgit v0.10.2 From b33c35b11e0049e3adbb58df6c83d6c9496f8210 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 18 Sep 2015 00:13:28 +0200 Subject: mtd: mxc_nand: Fix module autoload for OF platform driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Acked-by: Uwe Kleine-König Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 2426db8..ab281b6 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1458,6 +1458,7 @@ static const struct of_device_id mxcnd_dt_ids[] = { }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mxcnd_dt_ids); static int __init mxcnd_probe_dt(struct mxc_nand_host *host) { -- cgit v0.10.2 From e971affaf92d530b653122f08aa23e7384de170f Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Mon, 28 Sep 2015 22:56:51 +0200 Subject: mtd: nand: pxa3xx-nand: prevent DFI bus lockup on removal After the conversion of pxa architecture to common clock framework, the NAND clock can be disabled on driver exit. In this case, it happens that if the driver used the NAND and set the DFI arbitration bit, the next access to a static memory controller area, such as an ethernet card, will stall the system bus, and the core will be stalled forever. This is especially true on pxa31x SoCs, where the NDCR was augmented with a new bit to prevent this lockups by giving full ownership of the DFI arbiter to the SMC, in change SCr#6. Fix this by clearing the DFI arbritration bit in driver exit. This effectively prevents a lockup on zylonite when removing pxa3xx-nand module, and using ethernet afterwards. Signed-off-by: Robert Jarzmik Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index da563cd..232c707 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -76,7 +76,8 @@ #define NDCR_ND_MODE (0x3 << 21) #define NDCR_NAND_MODE (0x0) #define NDCR_CLR_PG_CNT (0x1 << 20) -#define NDCR_STOP_ON_UNCOR (0x1 << 19) +#define NFCV1_NDCR_ARB_CNTL (0x1 << 19) +#define NFCV2_NDCR_STOP_ON_UNCOR (0x1 << 19) #define NDCR_RD_ID_CNT_MASK (0x7 << 16) #define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) @@ -1320,7 +1321,8 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) /* Set an initial chunk size */ info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; - info->reg_ndcr = ndcr & ~NDCR_INT_MASK; + info->reg_ndcr = ndcr & + ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL); info->ndtr0cs0 = nand_readl(info, NDTR0CS0); info->ndtr1cs0 = nand_readl(info, NDTR1CS0); return 0; @@ -1553,6 +1555,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) pxa3xx_flash_ids[1].name = NULL; def = pxa3xx_flash_ids; KEEP_CONFIG: + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; if (info->reg_ndcr & NDCR_DWIDTH_M) chip->options |= NAND_BUSWIDTH_16; @@ -1768,6 +1771,16 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) free_irq(irq, info); pxa3xx_nand_free_buff(info); + /* + * In the pxa3xx case, the DFI bus is shared between the SMC and NFC. + * In order to prevent a lockup of the system bus, the DFI bus + * arbitration is granted to SMC upon driver removal. This is done by + * setting the x_ARB_CNTL bit, which also prevents the NAND to have + * access to the bus anymore. + */ + nand_writel(info, NDCR, + (nand_readl(info, NDCR) & ~NDCR_ND_ARB_EN) | + NFCV1_NDCR_ARB_CNTL); clk_disable_unprepare(info->clk); for (cs = 0; cs < pdata->num_cs; cs++) -- cgit v0.10.2 From 35667b9983a2a89e504377e89388982dc398773d Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Wed, 8 Jul 2015 17:15:34 +0200 Subject: mtd: Destroy mtd_idr on module_exit Destroy mtd_idr on module_exit, reclaiming the allocated memory. This was detected by the following semantic patch (written by Luis Rodriguez ) @ defines_module_init @ declarer name module_init, module_exit; declarer name DEFINE_IDR; identifier init; @@ module_init(init); @ defines_module_exit @ identifier exit; @@ module_exit(exit); @ declares_idr depends on defines_module_init && defines_module_exit @ identifier idr; @@ DEFINE_IDR(idr); @ on_exit_calls_destroy depends on declares_idr && defines_module_exit @ identifier declares_idr.idr, defines_module_exit.exit; @@ exit(void) { ... idr_destroy(&idr); ... } @ missing_module_idr_destroy depends on declares_idr && defines_module_exit && !on_exit_calls_destroy @ identifier declares_idr.idr, defines_module_exit.exit; @@ exit(void) { ... +idr_destroy(&idr); } Signed-off-by: Johannes Thumshirn Signed-off-by: Brian Norris diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 8bbbb75..8598e3b 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1301,6 +1301,7 @@ static void __exit cleanup_mtd(void) remove_proc_entry("mtd", NULL); class_unregister(&mtd_class); bdi_destroy(&mtd_bdi); + idr_destroy(&mtd_idr); } module_init(init_mtd); -- cgit v0.10.2 From adf508c347c3a5d66d7610412467e2a03924485b Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 9 Jul 2015 22:30:57 +0200 Subject: mtd: spi-nor: s25fl008k and s25fl016k supports dual/quad mode s25fl016k can be found on Embedded Artists' LPC4357 Developer's Kit where is used in quad mode by the LPC4357 SPIFI controller. Signed-off-by: Joachim Eastwood Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 8818d43..11c882a 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -618,8 +618,8 @@ static const struct flash_info spi_nor_ids[] = { { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, - { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, - { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, + { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) }, { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) }, -- cgit v0.10.2 From d855d23b56a5d67a7727dc275a86aaba290d7c55 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 19 Jun 2015 13:10:06 -0700 Subject: mtd: cmdlinepart: allow small partitions I'm not sure why we have a PAGE_SIZE restriction on this partition parser. If we really wanted the restriction, I would expect it to be a restriction for *all* parsers, so we'd move it to the MTD core At any rate, while small partitions may not be useful (they'll often be smaller than the eraseblock size and therefore can only be used read-only), they still have use as a read-only partition. This restriction is especially annoying because it aborts the entire MTD's cmdline parsing, leaving it unpartitioned. So, let's kill the restriction and only check for zero-sized partitions, which I expect we don't want to allow. Signed-off-by: Brian Norris diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index c850300..477923a 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -115,9 +115,8 @@ static struct mtd_partition * newpart(char *s, s++; } else { size = memparse(s, &s); - if (size < PAGE_SIZE) { - printk(KERN_ERR ERRP "partition size too small (%llx)\n", - size); + if (!size) { + printk(KERN_ERR ERRP "partition has size 0\n"); return ERR_PTR(-EINVAL); } } -- cgit v0.10.2 From ecb43e0a5f72379095892660c4e1601904ab1949 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 19 Jun 2015 13:10:07 -0700 Subject: mtd: cmdlinepart: convert printk() to pr_*() This driver uses some custom macros for printing. Let's use the standard pr_fmt()/pr_{err,warn}(). Signed-off-by: Brian Norris diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index 477923a..08f6298 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -48,6 +48,8 @@ * edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) */ +#define pr_fmt(fmt) "mtd: " fmt + #include #include #include @@ -55,9 +57,6 @@ #include #include -/* error message prefix */ -#define ERRP "mtd: " - /* debug macro */ #if 0 #define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0) @@ -116,7 +115,7 @@ static struct mtd_partition * newpart(char *s, } else { size = memparse(s, &s); if (!size) { - printk(KERN_ERR ERRP "partition has size 0\n"); + pr_err("partition has size 0\n"); return ERR_PTR(-EINVAL); } } @@ -141,7 +140,7 @@ static struct mtd_partition * newpart(char *s, name = ++s; p = strchr(name, delim); if (!p) { - printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); + pr_err("no closing %c found in partition name\n", delim); return ERR_PTR(-EINVAL); } name_len = p - name; @@ -169,7 +168,7 @@ static struct mtd_partition * newpart(char *s, /* test if more partitions are following */ if (*s == ',') { if (size == SIZE_REMAINING) { - printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n"); + pr_err("no partitions allowed after a fill-up partition\n"); return ERR_PTR(-EINVAL); } /* more partitions follow, parse them */ @@ -236,7 +235,7 @@ static int mtdpart_setup_real(char *s) /* fetch */ p = strchr(s, ':'); if (!p) { - printk(KERN_ERR ERRP "no mtd-id\n"); + pr_err("no mtd-id\n"); return -EINVAL; } mtd_id_len = p - mtd_id; @@ -288,7 +287,7 @@ static int mtdpart_setup_real(char *s) /* does another spec follow? */ if (*s != ';') { - printk(KERN_ERR ERRP "bad character after partition (%c)\n", *s); + pr_err("bad character after partition (%c)\n", *s); return -EINVAL; } s++; @@ -342,17 +341,15 @@ static int parse_cmdline_partitions(struct mtd_info *master, part->parts[i].size = master->size - offset; if (offset + part->parts[i].size > master->size) { - printk(KERN_WARNING ERRP - "%s: partitioning exceeds flash size, truncating\n", - part->mtd_id); + pr_warn("%s: partitioning exceeds flash size, truncating\n", + part->mtd_id); part->parts[i].size = master->size - offset; } offset += part->parts[i].size; if (part->parts[i].size == 0) { - printk(KERN_WARNING ERRP - "%s: skipping zero sized partition\n", - part->mtd_id); + pr_warn("%s: skipping zero sized partition\n", + part->mtd_id); part->num_parts--; memmove(&part->parts[i], &part->parts[i + 1], sizeof(*part->parts) * (part->num_parts - i)); -- cgit v0.10.2 From 16c863bbf4dabc1ff67d5c67d2d6e614211528d4 Mon Sep 17 00:00:00 2001 From: fangwei Date: Tue, 7 Jul 2015 10:54:26 +0800 Subject: jffs2: remove unneeded kfree c->oobbuf hasn't been kmalloced in jffs2_dataflash_setup, so there is no need to free it. Signed-off-by: Wei Fang Signed-off-by: Brian Norris diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 09ed551..63f31c0 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1274,7 +1274,6 @@ int jffs2_dataflash_setup(struct jffs2_sb_info *c) { #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf_verify) { - kfree(c->oobbuf); kfree(c->wbuf); return -ENOMEM; } -- cgit v0.10.2 From 9c07d094bbfe3d09e4c3e01a81cd1399bd23b268 Mon Sep 17 00:00:00 2001 From: Enrico Jorns Date: Fri, 18 Sep 2015 10:02:41 +0200 Subject: mtd: nand: denali: pass col argument to READID operation A read id operation followed by 0x00 reads the device ID while a read id operation followed by 0x20 reads the possible ONFI identifier. As the READID function did not propagate the second id parameter but had a hard-coded call for 0x90 0x00, reading the ONFI identifier was not possible and thus chips werde not detected (tested with MT29F8G08ABABAWP) Signed-off-by: Enrico Jorns Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 870c7fc..7b6186f 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1304,7 +1304,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, */ addr = MODE_11 | BANK(denali->flash_bank); index_addr(denali, addr | 0, 0x90); - index_addr(denali, addr | 1, 0); + index_addr(denali, addr | 1, col); for (i = 0; i < 8; i++) { index_addr_read_data(denali, addr | 2, &id); write_byte_to_buf(denali, id); -- cgit v0.10.2 From 271707b1d817f5104e02b2bd1bab43f0c8759418 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Tue, 21 Jul 2015 09:39:31 -0500 Subject: mtd: nand: denali: max_banks calculation changed in revision 5.1 Read Denali hardware revision number and use it to calculate max_banks, The encoding of max_banks changed in Denali revision 5.1. Signed-off-by: Graham Moore [Brian: parentheses around macro arg] Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 7b6186f..43c0771 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -458,8 +458,17 @@ static void find_valid_banks(struct denali_nand_info *denali) static void detect_max_banks(struct denali_nand_info *denali) { uint32_t features = ioread32(denali->flash_reg + FEATURES); + /* + * Read the revision register, so we can calculate the max_banks + * properly: the encoding changed from rev 5.0 to 5.1 + */ + u32 revision = MAKE_COMPARABLE_REVISION( + ioread32(denali->flash_reg + REVISION)); - denali->max_banks = 2 << (features & FEATURES__N_BANKS); + if (revision < REVISION_5_1) + denali->max_banks = 2 << (features & FEATURES__N_BANKS); + else + denali->max_banks = 1 << (features & FEATURES__N_BANKS); } static void detect_partition_feature(struct denali_nand_info *denali) diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 145bf88..4b12cd3 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -178,6 +178,8 @@ #define REVISION 0x370 #define REVISION__VALUE 0xffff +#define MAKE_COMPARABLE_REVISION(x) swab16((x) & REVISION__VALUE) +#define REVISION_5_1 0x00000501 #define ONFI_DEVICE_FEATURES 0x380 #define ONFI_DEVICE_FEATURES__VALUE 0x003f -- cgit v0.10.2 From 15c2bf2f3f5c1f25293ad89a9effeb75524458e8 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Fri, 18 Sep 2015 21:42:59 +0200 Subject: pcmciamtd: Add id for PRETEC 4MB SRAM The module pcmciamtd doesn't generate a mtd node for PRETEC 4MB SRAM cards without the id and hash added to pcmciamtd.c Tested on 3 different 4MB pretec sram cards. Signed-off-by: Tom Englund Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index af747af..3dad211 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -700,6 +700,7 @@ static const struct pcmcia_device_id pcmciamtd_ids[] = { PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0), PCMCIA_DEVICE_PROD_ID123("M-Systems", "M-SYS Flash Memory Card", "(c) M-Systems", 0x7ed2ad87, 0x675dc3fb, 0x7aef3965), PCMCIA_DEVICE_PROD_ID12("PRETEC", " 2MB SRAM CARD", 0xebf91155, 0x805360ca), + PCMCIA_DEVICE_PROD_ID12("PRETEC", " 4MB SRAM CARD", 0xebf91155, 0x20b6bf17), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad), PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies", " 4MB FLASH Card", 0x96fd8277, 0x737a5b05), -- cgit v0.10.2 From c887be71cc3984db486c4d942dea2682c7b24a71 Mon Sep 17 00:00:00 2001 From: Yao Yuan Date: Wed, 16 Sep 2015 17:59:45 +0800 Subject: mtd: spi-nor: Add support for sst25wf040b It is a 512KiB flash with 4 KiB erase sectors. Signed-off-by: Yuan Yao Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 11c882a..c616ff5 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -635,6 +635,7 @@ static const struct flash_info spi_nor_ids[] = { { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K) }, + { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) }, { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, -- cgit v0.10.2 From 09b6a377687b885565339e60bc62566433a0406f Mon Sep 17 00:00:00 2001 From: Furquan Shaikh Date: Fri, 18 Sep 2015 14:59:17 -0700 Subject: mtd: spi-nor: scale up timeout for full-chip erase This patch fixes timeout issues seen on large NOR flash (e.g., 16MB w25q128fw) when using ioctl(MEMERASE) with offset=0 and length=16M. The input parameters matter because spi_nor_erase() uses a different code path for full-chip erase, where we use the SPINOR_OP_CHIP_ERASE (0xc7) opcode. Fix: use a different timeout for full-chip erase than for other commands. While most operations can be expected to perform relatively similarly across a variety of NOR flash types and sizes (and therefore might as well use a similar timeout to keep things simple), full-chip erase is unique, because the time it typically takes to complete: (1) is much larger than most operations and (2) scales with the size of the flash. Let's base our timeout on the original comments stuck here -- that a 2MB flash requires max 40s to erase. Small survey of a few flash datasheets I have lying around: Chip Size (MB) Max chip erase (seconds) ---- -------- ------------------------ w25q32fw 4 50 w25q64cv 8 30 w25q64fw 8 100 w25q128fw 16 200 s25fl128s 16 ~256 s25fl256s 32 ~512 From this data, it seems plenty sufficient to say we need to wait for 40 seconds for each 2MB of flash. After this change, it might make some sense to decrease the timeout for everything else, as even the most extreme operations (single block erase?) shouldn't take more than a handful of seconds. But for safety, let's leave it as-is. It's only an error case, after all, so we don't exactly need to optimize it. Signed-off-by: Furquan Shaikh Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index c616ff5..14a851f 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -24,7 +25,18 @@ #include /* Define max times to check status register before we give up. */ -#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ + +/* + * For everything but full-chip erase; probably could be much smaller, but kept + * around for safety for now + */ +#define DEFAULT_READY_WAIT_JIFFIES (40UL * HZ) + +/* + * For full-chip erase, calibrated to a 2MB flash (M25P16); should be scaled up + * for larger flash + */ +#define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) #define SPI_NOR_MAX_ID_LEN 6 @@ -233,12 +245,13 @@ static int spi_nor_ready(struct spi_nor *nor) * Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error. */ -static int spi_nor_wait_till_ready(struct spi_nor *nor) +static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor, + unsigned long timeout_jiffies) { unsigned long deadline; int timeout = 0, ret; - deadline = jiffies + MAX_READY_WAIT_JIFFIES; + deadline = jiffies + timeout_jiffies; while (!timeout) { if (time_after_eq(jiffies, deadline)) @@ -258,6 +271,12 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor) return -ETIMEDOUT; } +static int spi_nor_wait_till_ready(struct spi_nor *nor) +{ + return spi_nor_wait_till_ready_with_timeout(nor, + DEFAULT_READY_WAIT_JIFFIES); +} + /* * Erase the whole flash memory * @@ -321,6 +340,8 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) /* whole-chip erase? */ if (len == mtd->size) { + unsigned long timeout; + write_enable(nor); if (erase_chip(nor)) { @@ -328,7 +349,16 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) goto erase_err; } - ret = spi_nor_wait_till_ready(nor); + /* + * Scale the timeout linearly with the size of the flash, with + * a minimum calibrated to an old 2MB flash. We could try to + * pull these from CFI/SFDP, but these values should be good + * enough for now. + */ + timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES, + CHIP_ERASE_2MB_READY_WAIT_JIFFIES * + (unsigned long)(mtd->size / SZ_2M)); + ret = spi_nor_wait_till_ready_with_timeout(nor, timeout); if (ret) goto erase_err; -- cgit v0.10.2 From 4404bd742db767a7f316b1207c46a04a38ca7198 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 18 Sep 2015 15:08:14 -0700 Subject: mtd: spi-nor: add support for w25q128fw Tested only with single I/O, but the datasheet says it supports dual and quad. Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 14a851f..4be41fb 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -718,6 +718,7 @@ static const struct flash_info spi_nor_ids[] = { { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "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 53bb724f94f57b67213e08a4c155c8c5eb74c644 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 21 Sep 2015 13:26:59 -0700 Subject: mtd: provide proper 32/64-bit compat_ioctl() support for BLKPG After a bit of poking around wondering why my 32-bit user-space can't seem to send a proper ioctl(BLKPG) to an MTD on my 64-bit kernel (ARM64), I noticed that struct blkpg_ioctl_arg is actually pretty unsuitable for use in the ioctl() ABI, due to its use of raw pointers, and its lack of alignment/packing restrictions (32-bit arch'es tend to pack the 4 fields into 4 32-bit words, whereas 64-bit arch'es would add padding after the third int, and make this 6 32-bit words). Anyway, this means BLKPG deserves some special compat_ioctl handling. Do the conversion in a small shim for MTD. block/compat_ioctl.c already has compat support for the block subsystem, but it does so by a re-marshalling data to/from user-space (see compat_blkpg_ioctl()). Personally, I think this approach is cleaner. Tested only on MTD, with an ARM32 user space on an ARM64 kernel. Signed-off-by: Brian Norris diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 55fa27e..6d19835 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -498,21 +498,17 @@ static int shrink_ecclayout(const struct nand_ecclayout *from, } static int mtdchar_blkpg_ioctl(struct mtd_info *mtd, - struct blkpg_ioctl_arg __user *arg) + struct blkpg_ioctl_arg *arg) { - struct blkpg_ioctl_arg a; struct blkpg_partition p; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) + if (copy_from_user(&p, arg->data, sizeof(p))) return -EFAULT; - if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) - return -EFAULT; - - switch (a.op) { + switch (arg->op) { case BLKPG_ADD_PARTITION: /* Only master mtd device must be used to add partitions */ @@ -966,8 +962,13 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) case BLKPG: { - ret = mtdchar_blkpg_ioctl(mtd, - (struct blkpg_ioctl_arg __user *)arg); + struct blkpg_ioctl_arg __user *blk_arg = argp; + struct blkpg_ioctl_arg a; + + if (copy_from_user(&a, blk_arg, sizeof(a))) + ret = -EFAULT; + else + ret = mtdchar_blkpg_ioctl(mtd, &a); break; } @@ -1046,6 +1047,29 @@ static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd, &buf_user->start); break; } + + case BLKPG: + { + /* Convert from blkpg_compat_ioctl_arg to blkpg_ioctl_arg */ + struct blkpg_compat_ioctl_arg __user *uarg = argp; + struct blkpg_compat_ioctl_arg compat_arg; + struct blkpg_ioctl_arg a; + + if (copy_from_user(&compat_arg, uarg, sizeof(compat_arg))) { + ret = -EFAULT; + break; + } + + memset(&a, 0, sizeof(a)); + a.op = compat_arg.op; + a.flags = compat_arg.flags; + a.datalen = compat_arg.datalen; + a.data = compat_ptr(compat_arg.data); + + ret = mtdchar_blkpg_ioctl(mtd, &a); + break; + } + default: ret = mtdchar_ioctl(file, cmd, (unsigned long)argp); } diff --git a/include/linux/blkpg.h b/include/linux/blkpg.h new file mode 100644 index 0000000..bef124f --- /dev/null +++ b/include/linux/blkpg.h @@ -0,0 +1,21 @@ +#ifndef _LINUX_BLKPG_H +#define _LINUX_BLKPG_H + +/* + * Partition table and disk geometry handling + */ + +#include +#include + +#ifdef CONFIG_COMPAT +/* For 32-bit/64-bit compatibility of struct blkpg_ioctl_arg */ +struct blkpg_compat_ioctl_arg { + compat_int_t op; + compat_int_t flags; + compat_int_t datalen; + compat_uptr_t data; +}; +#endif + +#endif /* _LINUX_BLKPG_H */ diff --git a/include/uapi/linux/blkpg.h b/include/uapi/linux/blkpg.h index a851944..63739a0 100644 --- a/include/uapi/linux/blkpg.h +++ b/include/uapi/linux/blkpg.h @@ -1,5 +1,5 @@ -#ifndef _LINUX_BLKPG_H -#define _LINUX_BLKPG_H +#ifndef _UAPI__LINUX_BLKPG_H +#define _UAPI__LINUX_BLKPG_H /* * Partition table and disk geometry handling @@ -56,4 +56,4 @@ struct blkpg_partition { char volname[BLKPG_VOLNAMELTH]; /* volume label */ }; -#endif /* _LINUX_BLKPG_H */ +#endif /* _UAPI__LINUX_BLKPG_H */ -- cgit v0.10.2 From 456930d80a2da129974640dc0238d5380597e172 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 2 Sep 2015 18:06:33 -0700 Subject: mtd: nand: vf610_nfc: Freescale NFC for VF610, MPC5125 and others This driver supports Freescale NFC (NAND flash controller) found on Vybrid (VF610), MPC5125, MCF54418 and Kinetis K70. The driver has been tested using 8-bit and 16-bit NAND interface on the ARM based Vybrid SoC VF500 and VF610 platform. parameter page reading. Limitations: - Untested on MPC5125 and M54418. - DMA and pipelining not used. - 2K pages or less. - No chip select, one NAND chip per controller. - No hardware ECC. Some paths have been hand-optimized and evaluated by measurements made using mtd_speedtest.ko on a 100MB MTD partition. Colibri VF50 eb write % eb read % page write % page read % rel/opt 5175 11537 4560 11039 opt 5164 -0.21 11420 -1.01 4737 +3.88 10918 -1.10 none 5113 -1.20 11352 -1.60 4490 -1.54 10865 -1.58 Colibri VF61 eb write % eb read % page write % page read % rel/opt 5766 13096 5459 12846 opt 5883 +2.03 13064 -0.24 5561 +1.87 12802 -0.34 none 5701 -1.13 12980 -0.89 5488 +0.53 12735 -0.86 rel = using readl_relaxed/writel_relaxed in optimized paths opt = hand-optimized by combining multiple accesses into one read/write The measurements have not been statistically verfied, hence use them with care. The author came to the conclusion that using the relaxed variants of readl/writel are not worth the additional code. Signed-off-by: Bill Pringlemeir Tested-by: Albert ARIBAUD Signed-off-by: Stefan Agner Reviewed-by: Alexey Klimov Signed-off-by: Brian Norris diff --git a/MAINTAINERS b/MAINTAINERS index 7ba7ab7..41e1e2b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11056,6 +11056,12 @@ S: Maintained F: Documentation/fb/uvesafb.txt F: drivers/video/fbdev/uvesafb.* +VF610 NAND DRIVER +M: Stefan Agner +L: linux-mtd@lists.infradead.org +S: Supported +F: drivers/mtd/nand/vf610_nfc.c + VFAT/FAT/MSDOS FILESYSTEM M: OGAWA Hirofumi S: Maintained diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 3324281..9f9736c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -460,6 +460,15 @@ config MTD_NAND_MPC5121_NFC This enables the driver for the NAND flash controller on the MPC5121 SoC. +config MTD_NAND_VF610_NFC + tristate "Support for Freescale NFC for VF610/MPC5125" + depends on (SOC_VF610 || COMPILE_TEST) + help + Enables support for NAND Flash Controller on some Freescale + processors like the VF610, MPC5125, MCF54418 or Kinetis K70. + The driver supports a maximum 2k page size. The driver + currently does not support hardware ECC. + config MTD_NAND_MXC tristate "MXC NAND support" depends on ARCH_MXC diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 075a027..2c7f014 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o +obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c new file mode 100644 index 0000000..90a48f4 --- /dev/null +++ b/drivers/mtd/nand/vf610_nfc.c @@ -0,0 +1,686 @@ +/* + * Copyright 2009-2015 Freescale Semiconductor, Inc. and others + * + * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver. + * Jason ported to M54418TWR and MVFA5 (VF610). + * Authors: Stefan Agner + * Bill Pringlemeir + * Shaohui Xie + * Jason Jin + * + * Based on original driver mpc5121_nfc.c. + * + * This 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. + * + * Limitations: + * - Untested on MPC5125 and M54418. + * - DMA and pipelining not used. + * - 2K pages or less. + * - No chip select, one NAND chip per controller. + * - No hardware ECC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "vf610_nfc" + +/* Register Offsets */ +#define NFC_FLASH_CMD1 0x3F00 +#define NFC_FLASH_CMD2 0x3F04 +#define NFC_COL_ADDR 0x3F08 +#define NFC_ROW_ADDR 0x3F0c +#define NFC_ROW_ADDR_INC 0x3F14 +#define NFC_FLASH_STATUS1 0x3F18 +#define NFC_FLASH_STATUS2 0x3F1c +#define NFC_CACHE_SWAP 0x3F28 +#define NFC_SECTOR_SIZE 0x3F2c +#define NFC_FLASH_CONFIG 0x3F30 +#define NFC_IRQ_STATUS 0x3F38 + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x1000) + +#define PAGE_2K 0x0800 +#define OOB_64 0x0040 +#define OOB_MAX 0x0100 + +/* + * NFC_CMD2[CODE] values. See section: + * - 31.4.7 Flash Command Code Description, Vybrid manual + * - 23.8.6 Flash Command Sequencer, MPC5125 manual + * + * Briefly these are bitmasks of controller cycles. + */ +#define READ_PAGE_CMD_CODE 0x7EE0 +#define READ_ONFI_PARAM_CMD_CODE 0x4860 +#define PROGRAM_PAGE_CMD_CODE 0x7FC0 +#define ERASE_CMD_CODE 0x4EC0 +#define READ_ID_CMD_CODE 0x4804 +#define RESET_CMD_CODE 0x4040 +#define STATUS_READ_CMD_CODE 0x4068 + +/* NFC ECC mode define */ +#define ECC_BYPASS 0 + +/*** Register Mask and bit definitions */ + +/* NFC_FLASH_CMD1 Field */ +#define CMD_BYTE2_MASK 0xFF000000 +#define CMD_BYTE2_SHIFT 24 + +/* NFC_FLASH_CM2 Field */ +#define CMD_BYTE1_MASK 0xFF000000 +#define CMD_BYTE1_SHIFT 24 +#define CMD_CODE_MASK 0x00FFFF00 +#define CMD_CODE_SHIFT 8 +#define BUFNO_MASK 0x00000006 +#define BUFNO_SHIFT 1 +#define START_BIT BIT(0) + +/* NFC_COL_ADDR Field */ +#define COL_ADDR_MASK 0x0000FFFF +#define COL_ADDR_SHIFT 0 + +/* NFC_ROW_ADDR Field */ +#define ROW_ADDR_MASK 0x00FFFFFF +#define ROW_ADDR_SHIFT 0 +#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000 +#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28 +#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000 +#define ROW_ADDR_CHIP_SEL_SHIFT 24 + +/* NFC_FLASH_STATUS2 Field */ +#define STATUS_BYTE1_MASK 0x000000FF + +/* NFC_FLASH_CONFIG Field */ +#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000 +#define CONFIG_ECC_SRAM_ADDR_SHIFT 22 +#define CONFIG_ECC_SRAM_REQ_BIT BIT(21) +#define CONFIG_DMA_REQ_BIT BIT(20) +#define CONFIG_ECC_MODE_MASK 0x000E0000 +#define CONFIG_ECC_MODE_SHIFT 17 +#define CONFIG_FAST_FLASH_BIT BIT(16) +#define CONFIG_16BIT BIT(7) +#define CONFIG_BOOT_MODE_BIT BIT(6) +#define CONFIG_ADDR_AUTO_INCR_BIT BIT(5) +#define CONFIG_BUFNO_AUTO_INCR_BIT BIT(4) +#define CONFIG_PAGE_CNT_MASK 0xF +#define CONFIG_PAGE_CNT_SHIFT 0 + +/* NFC_IRQ_STATUS Field */ +#define IDLE_IRQ_BIT BIT(29) +#define IDLE_EN_BIT BIT(20) +#define CMD_DONE_CLEAR_BIT BIT(18) +#define IDLE_CLEAR_BIT BIT(17) + +enum vf610_nfc_alt_buf { + ALT_BUF_DATA = 0, + ALT_BUF_ID = 1, + ALT_BUF_STAT = 2, + ALT_BUF_ONFI = 3, +}; + +enum vf610_nfc_variant { + NFC_VFC610 = 1, +}; + +struct vf610_nfc { + struct mtd_info mtd; + struct nand_chip chip; + struct device *dev; + void __iomem *regs; + struct completion cmd_done; + uint buf_offset; + int write_sz; + /* Status and ID are in alternate locations. */ + enum vf610_nfc_alt_buf alt_buf; + enum vf610_nfc_variant variant; + struct clk *clk; +}; + +#define mtd_to_nfc(_mtd) container_of(_mtd, struct vf610_nfc, mtd) + +static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg) +{ + return readl(nfc->regs + reg); +} + +static inline void vf610_nfc_write(struct vf610_nfc *nfc, uint reg, u32 val) +{ + writel(val, nfc->regs + reg); +} + +static inline void vf610_nfc_set(struct vf610_nfc *nfc, uint reg, u32 bits) +{ + vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) | bits); +} + +static inline void vf610_nfc_clear(struct vf610_nfc *nfc, uint reg, u32 bits) +{ + vf610_nfc_write(nfc, reg, vf610_nfc_read(nfc, reg) & ~bits); +} + +static inline void vf610_nfc_set_field(struct vf610_nfc *nfc, u32 reg, + u32 mask, u32 shift, u32 val) +{ + vf610_nfc_write(nfc, reg, + (vf610_nfc_read(nfc, reg) & (~mask)) | val << shift); +} + +static inline void vf610_nfc_memcpy(void *dst, const void __iomem *src, + size_t n) +{ + /* + * Use this accessor for the internal SRAM buffers. On the ARM + * Freescale Vybrid SoC it's known that the driver can treat + * the SRAM buffer as if it's memory. Other platform might need + * to treat the buffers differently. + * + * For the time being, use memcpy + */ + memcpy(dst, src, n); +} + +/* Clear flags for upcoming command */ +static inline void vf610_nfc_clear_status(struct vf610_nfc *nfc) +{ + u32 tmp = vf610_nfc_read(nfc, NFC_IRQ_STATUS); + + tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT; + vf610_nfc_write(nfc, NFC_IRQ_STATUS, tmp); +} + +static void vf610_nfc_done(struct vf610_nfc *nfc) +{ + unsigned long timeout = msecs_to_jiffies(100); + + /* + * Barrier is needed after this write. This write need + * to be done before reading the next register the first + * time. + * vf610_nfc_set implicates such a barrier by using writel + * to write to the register. + */ + vf610_nfc_set(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); + vf610_nfc_set(nfc, NFC_FLASH_CMD2, START_BIT); + + if (!wait_for_completion_timeout(&nfc->cmd_done, timeout)) + dev_warn(nfc->dev, "Timeout while waiting for BUSY.\n"); + + vf610_nfc_clear_status(nfc); +} + +static u8 vf610_nfc_get_id(struct vf610_nfc *nfc, int col) +{ + u32 flash_id; + + if (col < 4) { + flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS1); + flash_id >>= (3 - col) * 8; + } else { + flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS2); + flash_id >>= 24; + } + + return flash_id & 0xff; +} + +static u8 vf610_nfc_get_status(struct vf610_nfc *nfc) +{ + return vf610_nfc_read(nfc, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK; +} + +static void vf610_nfc_send_command(struct vf610_nfc *nfc, u32 cmd_byte1, + u32 cmd_code) +{ + u32 tmp; + + vf610_nfc_clear_status(nfc); + + tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD2); + tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK); + tmp |= cmd_byte1 << CMD_BYTE1_SHIFT; + tmp |= cmd_code << CMD_CODE_SHIFT; + vf610_nfc_write(nfc, NFC_FLASH_CMD2, tmp); +} + +static void vf610_nfc_send_commands(struct vf610_nfc *nfc, u32 cmd_byte1, + u32 cmd_byte2, u32 cmd_code) +{ + u32 tmp; + + vf610_nfc_send_command(nfc, cmd_byte1, cmd_code); + + tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD1); + tmp &= ~CMD_BYTE2_MASK; + tmp |= cmd_byte2 << CMD_BYTE2_SHIFT; + vf610_nfc_write(nfc, NFC_FLASH_CMD1, tmp); +} + +static irqreturn_t vf610_nfc_irq(int irq, void *data) +{ + struct mtd_info *mtd = data; + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT); + complete(&nfc->cmd_done); + + return IRQ_HANDLED; +} + +static void vf610_nfc_addr_cycle(struct vf610_nfc *nfc, int column, int page) +{ + if (column != -1) { + if (nfc->chip.options & NAND_BUSWIDTH_16) + column = column / 2; + vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK, + COL_ADDR_SHIFT, column); + } + if (page != -1) + vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK, + ROW_ADDR_SHIFT, page); +} + +static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size) +{ + vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size); +} + +static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0; + + nfc->buf_offset = max(column, 0); + nfc->alt_buf = ALT_BUF_DATA; + + switch (command) { + case NAND_CMD_SEQIN: + /* Use valid column/page from preread... */ + vf610_nfc_addr_cycle(nfc, column, page); + /* + * SEQIN => data => PAGEPROG sequence is done by the controller + * hence we do not need to issue the command here... + */ + return; + case NAND_CMD_PAGEPROG: + trfr_sz += nfc->write_sz; + vf610_nfc_transfer_size(nfc, trfr_sz); + vf610_nfc_send_commands(nfc, NAND_CMD_SEQIN, + command, PROGRAM_PAGE_CMD_CODE); + break; + + case NAND_CMD_RESET: + vf610_nfc_transfer_size(nfc, 0); + vf610_nfc_send_command(nfc, command, RESET_CMD_CODE); + break; + + case NAND_CMD_READOOB: + trfr_sz += mtd->oobsize; + column = mtd->writesize; + vf610_nfc_transfer_size(nfc, trfr_sz); + vf610_nfc_send_commands(nfc, NAND_CMD_READ0, + NAND_CMD_READSTART, READ_PAGE_CMD_CODE); + vf610_nfc_addr_cycle(nfc, column, page); + break; + + case NAND_CMD_READ0: + trfr_sz += mtd->writesize + mtd->oobsize; + vf610_nfc_transfer_size(nfc, trfr_sz); + vf610_nfc_send_commands(nfc, NAND_CMD_READ0, + NAND_CMD_READSTART, READ_PAGE_CMD_CODE); + vf610_nfc_addr_cycle(nfc, column, page); + break; + + case NAND_CMD_PARAM: + nfc->alt_buf = ALT_BUF_ONFI; + trfr_sz = 3 * sizeof(struct nand_onfi_params); + vf610_nfc_transfer_size(nfc, trfr_sz); + vf610_nfc_send_command(nfc, command, READ_ONFI_PARAM_CMD_CODE); + vf610_nfc_addr_cycle(nfc, -1, column); + break; + + case NAND_CMD_ERASE1: + vf610_nfc_transfer_size(nfc, 0); + vf610_nfc_send_commands(nfc, command, + NAND_CMD_ERASE2, ERASE_CMD_CODE); + vf610_nfc_addr_cycle(nfc, column, page); + break; + + case NAND_CMD_READID: + nfc->alt_buf = ALT_BUF_ID; + nfc->buf_offset = 0; + vf610_nfc_transfer_size(nfc, 0); + vf610_nfc_send_command(nfc, command, READ_ID_CMD_CODE); + vf610_nfc_addr_cycle(nfc, -1, column); + break; + + case NAND_CMD_STATUS: + nfc->alt_buf = ALT_BUF_STAT; + vf610_nfc_transfer_size(nfc, 0); + vf610_nfc_send_command(nfc, command, STATUS_READ_CMD_CODE); + break; + default: + return; + } + + vf610_nfc_done(nfc); + + nfc->write_sz = 0; +} + +static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + uint c = nfc->buf_offset; + + /* Alternate buffers are only supported through read_byte */ + WARN_ON(nfc->alt_buf); + + vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len); + + nfc->buf_offset += len; +} + +static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + uint c = nfc->buf_offset; + uint l; + + l = min_t(uint, len, mtd->writesize + mtd->oobsize - c); + vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l); + + nfc->write_sz += l; + nfc->buf_offset += l; +} + +static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + u8 tmp; + uint c = nfc->buf_offset; + + switch (nfc->alt_buf) { + case ALT_BUF_ID: + tmp = vf610_nfc_get_id(nfc, c); + break; + case ALT_BUF_STAT: + tmp = vf610_nfc_get_status(nfc); + break; +#ifdef __LITTLE_ENDIAN + case ALT_BUF_ONFI: + /* Reverse byte since the controller uses big endianness */ + c = nfc->buf_offset ^ 0x3; + /* fall-through */ +#endif + default: + tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c)); + break; + } + nfc->buf_offset++; + return tmp; +} + +static u16 vf610_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); + return tmp; +} + +/* If not provided, upper layers apply a fixed delay. */ +static int vf610_nfc_dev_ready(struct mtd_info *mtd) +{ + /* NFC handles R/B internally; always ready. */ + return 1; +} + +/* + * This function supports Vybrid only (MPC5125 would have full RB and four CS) + */ +static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + u32 tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR); + + /* Vybrid only (MPC5125 would have full RB and four CS) */ + if (nfc->variant != NFC_VFC610) + return; + + tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK); + + if (chip >= 0) { + tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; + tmp |= BIT(chip) << ROW_ADDR_CHIP_SEL_SHIFT; + } + + vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp); +} + +static const struct of_device_id vf610_nfc_dt_ids[] = { + { .compatible = "fsl,vf610-nfc", .data = (void *)NFC_VFC610 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vf610_nfc_dt_ids); + +static void vf610_nfc_preinit_controller(struct vf610_nfc *nfc) +{ + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT); + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT); + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT); + vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT); + + /* Disable virtual pages, only one elementary transfer unit */ + vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, + CONFIG_PAGE_CNT_SHIFT, 1); +} + +static void vf610_nfc_init_controller(struct vf610_nfc *nfc) +{ + if (nfc->chip.options & NAND_BUSWIDTH_16) + vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); + else + vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); +} + +static int vf610_nfc_probe(struct platform_device *pdev) +{ + struct vf610_nfc *nfc; + struct resource *res; + struct mtd_info *mtd; + struct nand_chip *chip; + struct device_node *child; + const struct of_device_id *of_id; + int err; + int irq; + + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->dev = &pdev->dev; + mtd = &nfc->mtd; + chip = &nfc->chip; + + mtd->priv = chip; + mtd->owner = THIS_MODULE; + mtd->dev.parent = nfc->dev; + mtd->name = DRV_NAME; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->regs = devm_ioremap_resource(nfc->dev, res); + if (IS_ERR(nfc->regs)) + return PTR_ERR(nfc->regs); + + nfc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(nfc->clk)) + return PTR_ERR(nfc->clk); + + err = clk_prepare_enable(nfc->clk); + if (err) { + dev_err(nfc->dev, "Unable to enable clock!\n"); + return err; + } + + of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev); + nfc->variant = (enum vf610_nfc_variant)of_id->data; + + for_each_available_child_of_node(nfc->dev->of_node, child) { + if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) { + + if (chip->flash_node) { + dev_err(nfc->dev, + "Only one NAND chip supported!\n"); + err = -EINVAL; + goto error; + } + + chip->flash_node = child; + } + } + + if (!chip->flash_node) { + dev_err(nfc->dev, "NAND chip sub-node missing!\n"); + err = -ENODEV; + goto err_clk; + } + + chip->dev_ready = vf610_nfc_dev_ready; + chip->cmdfunc = vf610_nfc_command; + chip->read_byte = vf610_nfc_read_byte; + chip->read_word = vf610_nfc_read_word; + chip->read_buf = vf610_nfc_read_buf; + chip->write_buf = vf610_nfc_write_buf; + chip->select_chip = vf610_nfc_select_chip; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + + init_completion(&nfc->cmd_done); + + err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd); + if (err) { + dev_err(nfc->dev, "Error requesting IRQ!\n"); + goto error; + } + + vf610_nfc_preinit_controller(nfc); + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1, NULL)) { + err = -ENXIO; + goto error; + } + + vf610_nfc_init_controller(nfc); + + /* Bad block options. */ + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + /* Single buffer only, max 256 OOB minus ECC status */ + if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { + dev_err(nfc->dev, "Unsupported flash page size\n"); + err = -ENXIO; + goto error; + } + + /* second phase scan */ + if (nand_scan_tail(mtd)) { + err = -ENXIO; + goto error; + } + + platform_set_drvdata(pdev, mtd); + + /* Register device in MTD */ + return mtd_device_parse_register(mtd, NULL, + &(struct mtd_part_parser_data){ + .of_node = chip->flash_node, + }, + NULL, 0); + +error: + of_node_put(chip->flash_node); +err_clk: + clk_disable_unprepare(nfc->clk); + return err; +} + +static int vf610_nfc_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + nand_release(mtd); + clk_disable_unprepare(nfc->clk); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int vf610_nfc_suspend(struct device *dev) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + clk_disable_unprepare(nfc->clk); + return 0; +} + +static int vf610_nfc_resume(struct device *dev) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + pinctrl_pm_select_default_state(dev); + + clk_prepare_enable(nfc->clk); + + vf610_nfc_preinit_controller(nfc); + vf610_nfc_init_controller(nfc); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(vf610_nfc_pm_ops, vf610_nfc_suspend, vf610_nfc_resume); + +static struct platform_driver vf610_nfc_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = vf610_nfc_dt_ids, + .pm = &vf610_nfc_pm_ops, + }, + .probe = vf610_nfc_probe, + .remove = vf610_nfc_remove, +}; + +module_platform_driver(vf610_nfc_driver); + +MODULE_AUTHOR("Stefan Agner "); +MODULE_DESCRIPTION("Freescale VF610/MPC5125 NFC MTD NAND driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 049f42509973b86f0015e2c1c38d4c79aea408f5 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 2 Sep 2015 18:06:34 -0700 Subject: mtd: nand: vf610_nfc: add hardware BCH-ECC support This adds hardware ECC support using the BCH encoder in the NFC IP. The ECC encoder supports up to 32-bit correction by using 60 error correction bytes. There is no sub-page ECC step, ECC is calculated always across the whole page (up to 2k pages). Limitations: - HW ECC: Only 2K page with 64+ OOB. - HW ECC: Only 24 and 32-bit error correction implemented. Raw writes have been tested using the generic nand_write_page_raw implementation. However, raw reads are currently not possible because the controller need to know whether we are going to use the ECC mode already at NAND_CMD_READ0 command time. At this point we do not have the information whether it is a raw read or a regular read at driver level... Signed-off-by: Bill Pringlemeir Signed-off-by: Stefan Agner Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 9f9736c..ccd1158 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -466,8 +466,10 @@ config MTD_NAND_VF610_NFC help Enables support for NAND Flash Controller on some Freescale processors like the VF610, MPC5125, MCF54418 or Kinetis K70. - The driver supports a maximum 2k page size. The driver - currently does not support hardware ECC. + The driver supports a maximum 2k page size. With 2k pages and + 64 bytes or more of OOB, hardware ECC with up to 32-bit error + correction is supported. Hardware ECC is only enabled through + device tree. config MTD_NAND_MXC tristate "MXC NAND support" diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 90a48f4..42dad8e 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -19,8 +19,8 @@ * - Untested on MPC5125 and M54418. * - DMA and pipelining not used. * - 2K pages or less. - * - No chip select, one NAND chip per controller. - * - No hardware ECC. + * - HW ECC: Only 2K page with 64+ OOB. + * - HW ECC: Only 24 and 32-bit error correction implemented. */ #include @@ -77,6 +77,8 @@ /* NFC ECC mode define */ #define ECC_BYPASS 0 +#define ECC_45_BYTE 6 +#define ECC_60_BYTE 7 /*** Register Mask and bit definitions */ @@ -129,6 +131,18 @@ #define CMD_DONE_CLEAR_BIT BIT(18) #define IDLE_CLEAR_BIT BIT(17) +/* + * ECC status - seems to consume 8 bytes (double word). The documented + * status byte is located in the lowest byte of the second word (which is + * the 4th or 7th byte depending on endianness). + * Calculate an offset to store the ECC status at the end of the buffer. + */ +#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8) + +#define ECC_STATUS 0x4 +#define ECC_STATUS_MASK 0x80 +#define ECC_STATUS_ERR_COUNT 0x3F + enum vf610_nfc_alt_buf { ALT_BUF_DATA = 0, ALT_BUF_ID = 1, @@ -152,10 +166,40 @@ struct vf610_nfc { enum vf610_nfc_alt_buf alt_buf; enum vf610_nfc_variant variant; struct clk *clk; + bool use_hw_ecc; + u32 ecc_mode; }; #define mtd_to_nfc(_mtd) container_of(_mtd, struct vf610_nfc, mtd) +static struct nand_ecclayout vf610_nfc_ecc45 = { + .eccbytes = 45, + .eccpos = {19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { + {.offset = 2, + .length = 17} } +}; + +static struct nand_ecclayout vf610_nfc_ecc60 = { + .eccbytes = 60, + .eccpos = { 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63 }, + .oobfree = { + {.offset = 2, + .length = 2} } +}; + static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg) { return readl(nfc->regs + reg); @@ -297,6 +341,13 @@ static void vf610_nfc_addr_cycle(struct vf610_nfc *nfc, int column, int page) ROW_ADDR_SHIFT, page); } +static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode) +{ + vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, + CONFIG_ECC_MODE_MASK, + CONFIG_ECC_MODE_SHIFT, ecc_mode); +} + static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size) { vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size); @@ -315,6 +366,8 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_SEQIN: /* Use valid column/page from preread... */ vf610_nfc_addr_cycle(nfc, column, page); + nfc->buf_offset = 0; + /* * SEQIN => data => PAGEPROG sequence is done by the controller * hence we do not need to issue the command here... @@ -325,6 +378,10 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, vf610_nfc_transfer_size(nfc, trfr_sz); vf610_nfc_send_commands(nfc, NAND_CMD_SEQIN, command, PROGRAM_PAGE_CMD_CODE); + if (nfc->use_hw_ecc) + vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); + else + vf610_nfc_ecc_mode(nfc, ECC_BYPASS); break; case NAND_CMD_RESET: @@ -339,6 +396,7 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, vf610_nfc_send_commands(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, READ_PAGE_CMD_CODE); vf610_nfc_addr_cycle(nfc, column, page); + vf610_nfc_ecc_mode(nfc, ECC_BYPASS); break; case NAND_CMD_READ0: @@ -347,6 +405,7 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, vf610_nfc_send_commands(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, READ_PAGE_CMD_CODE); vf610_nfc_addr_cycle(nfc, column, page); + vf610_nfc_ecc_mode(nfc, nfc->ecc_mode); break; case NAND_CMD_PARAM: @@ -355,6 +414,7 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, vf610_nfc_transfer_size(nfc, trfr_sz); vf610_nfc_send_command(nfc, command, READ_ONFI_PARAM_CMD_CODE); vf610_nfc_addr_cycle(nfc, -1, column); + vf610_nfc_ecc_mode(nfc, ECC_BYPASS); break; case NAND_CMD_ERASE1: @@ -383,6 +443,7 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, vf610_nfc_done(nfc); + nfc->use_hw_ecc = false; nfc->write_sz = 0; } @@ -477,6 +538,94 @@ static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp); } +/* Count the number of 0's in buff up to max_bits */ +static inline int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + uint32_t *buff32 = (uint32_t *)buff; + int k, written_bits = 0; + + for (k = 0; k < (size / 4); k++) { + written_bits += hweight32(~buff32[k]); + if (unlikely(written_bits > max_bits)) + break; + } + + return written_bits; +} + +static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *oob, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS; + u8 ecc_status; + u8 ecc_count; + int flips; + int flips_threshold = nfc->chip.ecc.strength / 2; + + ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff; + ecc_count = ecc_status & ECC_STATUS_ERR_COUNT; + + if (!(ecc_status & ECC_STATUS_MASK)) + return ecc_count; + + /* Read OOB without ECC unit enabled */ + vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page); + vf610_nfc_read_buf(mtd, oob, mtd->oobsize); + + /* + * On an erased page, bit count (including OOB) should be zero or + * at least less then half of the ECC strength. + */ + flips = count_written_bits(dat, nfc->chip.ecc.size, flips_threshold); + flips += count_written_bits(oob, mtd->oobsize, flips_threshold); + + if (unlikely(flips > flips_threshold)) + return -EINVAL; + + /* Erased page. */ + memset(dat, 0xff, nfc->chip.ecc.size); + memset(oob, 0xff, mtd->oobsize); + return flips; +} + +static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int eccsize = chip->ecc.size; + int stat; + + vf610_nfc_read_buf(mtd, buf, eccsize); + if (oob_required) + vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page); + + if (stat < 0) { + mtd->ecc_stats.failed++; + return 0; + } else { + mtd->ecc_stats.corrected += stat; + return stat; + } +} + +static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + vf610_nfc_write_buf(mtd, buf, mtd->writesize); + if (oob_required) + vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Always write whole page including OOB due to HW ECC */ + nfc->use_hw_ecc = true; + nfc->write_sz = mtd->writesize + mtd->oobsize; + + return 0; +} + static const struct of_device_id vf610_nfc_dt_ids[] = { { .compatible = "fsl,vf610-nfc", .data = (void *)NFC_VFC610 }, { /* sentinel */ } @@ -503,6 +652,17 @@ static void vf610_nfc_init_controller(struct vf610_nfc *nfc) vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); else vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); + + if (nfc->chip.ecc.mode == NAND_ECC_HW) { + /* Set ECC status offset in SRAM */ + vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, + CONFIG_ECC_SRAM_ADDR_MASK, + CONFIG_ECC_SRAM_ADDR_SHIFT, + ECC_SRAM_ADDR >> 3); + + /* Enable ECC status in SRAM */ + vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT); + } } static int vf610_nfc_probe(struct platform_device *pdev) @@ -610,6 +770,45 @@ static int vf610_nfc_probe(struct platform_device *pdev) goto error; } + if (chip->ecc.mode == NAND_ECC_HW) { + if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { + dev_err(nfc->dev, "Unsupported flash with hwecc\n"); + err = -ENXIO; + goto error; + } + + if (chip->ecc.size != mtd->writesize) { + dev_err(nfc->dev, "Step size needs to be page size\n"); + err = -ENXIO; + goto error; + } + + /* Only 64 byte ECC layouts known */ + if (mtd->oobsize > 64) + mtd->oobsize = 64; + + if (chip->ecc.strength == 32) { + nfc->ecc_mode = ECC_60_BYTE; + chip->ecc.bytes = 60; + chip->ecc.layout = &vf610_nfc_ecc60; + } else if (chip->ecc.strength == 24) { + nfc->ecc_mode = ECC_45_BYTE; + chip->ecc.bytes = 45; + chip->ecc.layout = &vf610_nfc_ecc45; + } else { + dev_err(nfc->dev, "Unsupported ECC strength\n"); + err = -ENXIO; + goto error; + } + + /* propagate ecc.layout to mtd_info */ + mtd->ecclayout = chip->ecc.layout; + chip->ecc.read_page = vf610_nfc_read_page; + chip->ecc.write_page = vf610_nfc_write_page; + + chip->ecc.size = PAGE_2K; + } + /* second phase scan */ if (nand_scan_tail(mtd)) { err = -ENXIO; -- cgit v0.10.2 From 8a799590005180b1010912be096c1302e2bd06da Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 2 Sep 2015 18:06:35 -0700 Subject: mtd: nand: vf610_nfc: add device tree bindings Signed-off-by: Bill Pringlemeir Acked-by: Shawn Guo Signed-off-by: Stefan Agner [Brian: fixup #size-cells in example] Signed-off-by: Brian Norris diff --git a/Documentation/devicetree/bindings/mtd/vf610-nfc.txt b/Documentation/devicetree/bindings/mtd/vf610-nfc.txt new file mode 100644 index 0000000..c96eeb6 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/vf610-nfc.txt @@ -0,0 +1,59 @@ +Freescale's NAND flash controller (NFC) + +This variant of the Freescale NAND flash controller (NFC) can be found on +Vybrid (vf610), MPC5125, MCF54418 and Kinetis K70. + +Required properties: +- compatible: Should be set to "fsl,vf610-nfc". +- reg: address range of the NFC. +- interrupts: interrupt of the NFC. +- #address-cells: shall be set to 1. Encode the nand CS. +- #size-cells : shall be set to 0. +- assigned-clocks: main clock from the SoC, for Vybrid <&clks VF610_CLK_NFC>; +- assigned-clock-rates: The NAND bus timing is derived from this clock + rate and should not exceed maximum timing for any NAND memory chip + in a board stuffing. Typical NAND memory timings derived from this + clock are found in the SoC hardware reference manual. Furthermore, + there might be restrictions on maximum rates when using hardware ECC. + +- #address-cells, #size-cells : Must be present if the device has sub-nodes + representing partitions. + +Required children nodes: +Children nodes represent the available nand chips. Currently the driver can +only handle one NAND chip. + +Required properties: +- compatible: Should be set to "fsl,vf610-nfc-cs". +- nand-bus-width: see nand.txt +- nand-ecc-mode: see nand.txt + +Required properties for hardware ECC: +- nand-ecc-strength: supported strengths are 24 and 32 bit (see nand.txt) +- nand-ecc-step-size: step size equals page size, currently only 2k pages are + supported +- nand-on-flash-bbt: see nand.txt + +Example: + + nfc: nand@400e0000 { + compatible = "fsl,vf610-nfc"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x400e0000 0x4000>; + interrupts = ; + clocks = <&clks VF610_CLK_NFC>; + clock-names = "nfc"; + assigned-clocks = <&clks VF610_CLK_NFC>; + assigned-clock-rates = <33000000>; + + nand@0 { + compatible = "fsl,vf610-nfc-nandcs"; + reg = <0>; + nand-bus-width = <8>; + nand-ecc-mode = "hw"; + nand-ecc-strength = <32>; + nand-ecc-step-size = <2048>; + nand-on-flash-bbt; + }; + }; -- cgit v0.10.2 From 7827e3acad2df1c6537e5fe7211d216dabc60399 Mon Sep 17 00:00:00 2001 From: Dongsheng Yang Date: Tue, 21 Jul 2015 16:30:20 +0800 Subject: mtd: mtdram: check offs and len in mtdram->erase We should prevent user to erasing mtd device with an unaligned offset or length. Signed-off-by: Dongsheng Yang Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 8e28508..73fa297 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -32,8 +32,29 @@ MODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); // We could store these in the mtd structure, but we only support 1 device.. static struct mtd_info *mtd_info; +static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + + /* Start address must align on block boundary */ + if (ofs % mtd->erasesize) { + pr_debug("%s: unaligned address\n", __func__); + ret = -EINVAL; + } + + /* Length must align on block boundary */ + if (len % mtd->erasesize) { + pr_debug("%s: length not block aligned\n", __func__); + ret = -EINVAL; + } + + return ret; +} + static int ram_erase(struct mtd_info *mtd, struct erase_info *instr) { + if (check_offs_len(mtd, instr->addr, instr->len)) + return -EINVAL; memset((char *)mtd->priv + instr->addr, 0xff, instr->len); instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); -- cgit v0.10.2 From d489ff42db9179647004ca2b12c614cb71ac81ea Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 29 Sep 2015 17:28:44 -0700 Subject: Revert "mtd: mtdram: check offs and len in mtdram->erase" This reverts commit 7827e3acad2df1c6537e5fe7211d216dabc60399. There are some 64-bit arithmetic issues on some architectures, so let's wait until we get a better patch for this. Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 73fa297..8e28508 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -32,29 +32,8 @@ MODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); // We could store these in the mtd structure, but we only support 1 device.. static struct mtd_info *mtd_info; -static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - int ret = 0; - - /* Start address must align on block boundary */ - if (ofs % mtd->erasesize) { - pr_debug("%s: unaligned address\n", __func__); - ret = -EINVAL; - } - - /* Length must align on block boundary */ - if (len % mtd->erasesize) { - pr_debug("%s: length not block aligned\n", __func__); - ret = -EINVAL; - } - - return ret; -} - static int ram_erase(struct mtd_info *mtd, struct erase_info *instr) { - if (check_offs_len(mtd, instr->addr, instr->len)) - return -EINVAL; memset((char *)mtd->priv + instr->addr, 0xff, instr->len); instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); -- cgit v0.10.2 From e5bae86797141e4a95e42d825f737cb36d7b8c37 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 30 Jul 2015 12:18:03 +0200 Subject: mtd: mtdpart: fix add_mtd_partitions error path If we fail to allocate a partition structure in the middle of the partition creation process, the already allocated partitions are never removed, which means they are still present in the partition list and their resources are never freed. Signed-off-by: Boris Brezillon Cc: stable@vger.kernel.org Signed-off-by: Brian Norris diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index cafdb88..919a936 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -664,8 +664,10 @@ int add_mtd_partitions(struct mtd_info *master, for (i = 0; i < nbparts; i++) { slave = allocate_partition(master, parts + i, i, cur_offset); - if (IS_ERR(slave)) + if (IS_ERR(slave)) { + del_mtd_partitions(master); return PTR_ERR(slave); + } mutex_lock(&mtd_partitions_mutex); list_add(&slave->list, &mtd_partitions); -- cgit v0.10.2 From 039353c8f92ec22c8eb9ec3853b8289b8237bfc1 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 30 Sep 2015 09:54:26 -0700 Subject: mtd: nand: vf610_nfc: include missing pincrl/consumer.h This must have been implicitly included on the builds I tested. Reported by numerous test bots: drivers/mtd/nand/vf610_nfc.c: In function 'vf610_nfc_resume': drivers/mtd/nand/vf610_nfc.c:660:2: error: implicit declaration of function 'pinctrl_pm_select_default_state' [-Werror=implicit-function-declaration] pinctrl_pm_select_default_state(dev); ^ Signed-off-by: Brian Norris Acked-by: Stefan Agner diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 42dad8e..ae1f84e 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From b6a02c0847f80968869bea57824f00fa395057a0 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 16 Sep 2015 09:46:36 +0200 Subject: mtd: nand: sunxi: rework macros Suffix mask macros with _MSK and add new helper macros to avoid manually shifting values. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index ef1fe61..dc44435 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -57,11 +57,8 @@ #define NFC_REG_ECC_CTL 0x0034 #define NFC_REG_ECC_ST 0x0038 #define NFC_REG_DEBUG 0x003C -#define NFC_REG_ECC_CNT0 0x0040 -#define NFC_REG_ECC_CNT1 0x0044 -#define NFC_REG_ECC_CNT2 0x0048 -#define NFC_REG_ECC_CNT3 0x004c -#define NFC_REG_USER_DATA_BASE 0x0050 +#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) +#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) #define NFC_REG_SPARE_AREA 0x00A0 #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -69,12 +66,16 @@ /* define bit use in NFC_CTL */ #define NFC_EN BIT(0) #define NFC_RESET BIT(1) -#define NFC_BUS_WIDYH BIT(2) -#define NFC_RB_SEL BIT(3) -#define NFC_CE_SEL GENMASK(26, 24) +#define NFC_BUS_WIDTH_MSK BIT(2) +#define NFC_BUS_WIDTH_8 (0 << 2) +#define NFC_BUS_WIDTH_16 (1 << 2) +#define NFC_RB_SEL_MSK BIT(3) +#define NFC_RB_SEL(x) ((x) << 3) +#define NFC_CE_SEL_MSK GENMASK(26, 24) +#define NFC_CE_SEL(x) ((x) << 24) #define NFC_CE_CTL BIT(6) -#define NFC_CE_CTL1 BIT(7) -#define NFC_PAGE_SIZE GENMASK(11, 8) +#define NFC_PAGE_SHIFT_MSK GENMASK(11, 8) +#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) #define NFC_SAM BIT(12) #define NFC_RAM_METHOD BIT(14) #define NFC_DEBUG_CTL BIT(31) @@ -86,10 +87,7 @@ #define NFC_CMD_FIFO_STATUS BIT(3) #define NFC_STA BIT(4) #define NFC_NATCH_INT_FLAG BIT(5) -#define NFC_RB_STATE0 BIT(8) -#define NFC_RB_STATE1 BIT(9) -#define NFC_RB_STATE2 BIT(10) -#define NFC_RB_STATE3 BIT(11) +#define NFC_RB_STATE(x) BIT(x + 8) /* define bit use in NFC_INT */ #define NFC_B2R_INT_ENABLE BIT(0) @@ -109,9 +107,11 @@ (((tCAD) & 0x7) << 8)) /* define bit use in NFC_CMD */ -#define NFC_CMD_LOW_BYTE GENMASK(7, 0) -#define NFC_CMD_HIGH_BYTE GENMASK(15, 8) -#define NFC_ADR_NUM GENMASK(18, 16) +#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0) +#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) +#define NFC_CMD(x) (x) +#define NFC_ADR_NUM_MSK GENMASK(18, 16) +#define NFC_ADR_NUM(x) (((x) - 1) << 16) #define NFC_SEND_ADR BIT(19) #define NFC_ACCESS_DIR BIT(20) #define NFC_DATA_TRANS BIT(21) @@ -123,29 +123,38 @@ #define NFC_ROW_AUTO_INC BIT(27) #define NFC_SEND_CMD3 BIT(28) #define NFC_SEND_CMD4 BIT(29) -#define NFC_CMD_TYPE GENMASK(31, 30) +#define NFC_CMD_TYPE_MSK GENMASK(31, 30) +#define NFC_NORMAL_OP (0 << 30) +#define NFC_ECC_OP (1 << 30) +#define NFC_PAGE_OP (2 << 30) /* define bit use in NFC_RCMD_SET */ -#define NFC_READ_CMD GENMASK(7, 0) -#define NFC_RANDOM_READ_CMD0 GENMASK(15, 8) -#define NFC_RANDOM_READ_CMD1 GENMASK(23, 16) +#define NFC_READ_CMD_MSK GENMASK(7, 0) +#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8) +#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16) /* define bit use in NFC_WCMD_SET */ -#define NFC_PROGRAM_CMD GENMASK(7, 0) -#define NFC_RANDOM_WRITE_CMD GENMASK(15, 8) -#define NFC_READ_CMD0 GENMASK(23, 16) -#define NFC_READ_CMD1 GENMASK(31, 24) +#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0) +#define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8) +#define NFC_READ_CMD0_MSK GENMASK(23, 16) +#define NFC_READ_CMD1_MSK GENMASK(31, 24) /* define bit use in NFC_ECC_CTL */ #define NFC_ECC_EN BIT(0) #define NFC_ECC_PIPELINE BIT(3) #define NFC_ECC_EXCEPTION BIT(4) -#define NFC_ECC_BLOCK_SIZE BIT(5) +#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) #define NFC_RANDOM_EN BIT(9) #define NFC_RANDOM_DIRECTION BIT(10) -#define NFC_ECC_MODE_SHIFT 12 -#define NFC_ECC_MODE GENMASK(15, 12) -#define NFC_RANDOM_SEED GENMASK(30, 16) +#define NFC_ECC_MODE_MSK GENMASK(15, 12) +#define NFC_ECC_MODE(x) ((x) << 12) +#define NFC_RANDOM_SEED_MSK GENMASK(30, 16) +#define NFC_RANDOM_SEED(x) ((x) << 16) + +/* define bit use in NFC_ECC_ST */ +#define NFC_ECC_ERR(x) BIT(x) +#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) +#define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff) /* NFC_USER_DATA helper macros */ #define NFC_BUF_TO_USER_DATA(buf) ((buf)[0] | ((buf)[1] << 8) | \ @@ -360,13 +369,13 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd) switch (rb->type) { case RB_NATIVE: ret = !!(readl(nfc->regs + NFC_REG_ST) & - (NFC_RB_STATE0 << rb->info.nativeid)); + NFC_RB_STATE(rb->info.nativeid)); if (ret) break; sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo); ret = !!(readl(nfc->regs + NFC_REG_ST) & - (NFC_RB_STATE0 << rb->info.nativeid)); + NFC_RB_STATE(rb->info.nativeid)); break; case RB_GPIO: ret = gpio_get_value(rb->info.gpio); @@ -396,19 +405,19 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) return; ctl = readl(nfc->regs + NFC_REG_CTL) & - ~(NFC_CE_SEL | NFC_RB_SEL | NFC_EN); + ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); if (chip >= 0) { sel = &sunxi_nand->sels[chip]; - ctl |= (sel->cs << 24) | NFC_EN | - (((nand->page_shift - 10) & 0xf) << 8); + ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | + NFC_PAGE_SHIFT(nand->page_shift - 10); if (sel->rb.type == RB_NONE) { nand->dev_ready = NULL; } else { nand->dev_ready = sunxi_nfc_dev_ready; if (sel->rb.type == RB_NATIVE) - ctl |= (sel->rb.info.nativeid << 3); + ctl |= NFC_RB_SEL(sel->rb.info.nativeid); } writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); @@ -550,9 +559,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, int cnt; tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); - tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | - NFC_ECC_EXCEPTION; + tmp &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); + tmp |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; writel(tmp, nfc->regs + NFC_REG_ECC_CTL); @@ -570,7 +578,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, if (ret) return ret; - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30); + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP; writel(tmp, nfc->regs + NFC_REG_CMD); ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); @@ -580,11 +588,11 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, memcpy_fromio(buf + (i * ecc->size), nfc->regs + NFC_RAM0_BASE, ecc->size); - if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) { + if (readl(nfc->regs + NFC_REG_ECC_ST) & NFC_ECC_ERR(0)) { mtd->ecc_stats.failed++; } else { - tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff; - mtd->ecc_stats.corrected += tmp; + tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)); + mtd->ecc_stats.corrected += NFC_ECC_ERR_CNT(0, tmp); max_bitflips = max_t(unsigned int, max_bitflips, tmp); } @@ -635,9 +643,8 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, int cnt; tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); - tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | - NFC_ECC_EXCEPTION; + tmp &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); + tmp |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; writel(tmp, nfc->regs + NFC_REG_ECC_CTL); @@ -652,7 +659,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, /* Fill OOB data in */ writel(NFC_BUF_TO_USER_DATA(chip->oob_poi + layout->oobfree[i].offset), - nfc->regs + NFC_REG_USER_DATA_BASE); + nfc->regs + NFC_REG_USER_DATA(0)); chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); @@ -661,7 +668,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, return ret; tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | - (1 << 30); + NFC_ECC_OP; writel(tmp, nfc->regs + NFC_REG_CMD); ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); if (ret) @@ -704,16 +711,15 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, int i; tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); - tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | - NFC_ECC_EXCEPTION; + tmp &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); + tmp |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; writel(tmp, nfc->regs + NFC_REG_ECC_CTL); for (i = 0; i < ecc->steps; i++) { chip->read_buf(mtd, NULL, ecc->size); - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30); + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP; writel(tmp, nfc->regs + NFC_REG_CMD); ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); @@ -724,11 +730,11 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, buf += ecc->size; offset += ecc->size; - if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) { + if (readl(nfc->regs + NFC_REG_ECC_ST) & NFC_ECC_ERR(0)) { mtd->ecc_stats.failed++; } else { - tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff; - mtd->ecc_stats.corrected += tmp; + tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)); + mtd->ecc_stats.corrected += NFC_ECC_ERR_CNT(0, tmp); max_bitflips = max_t(unsigned int, max_bitflips, tmp); } @@ -771,9 +777,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, int i; tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); - tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | - NFC_ECC_EXCEPTION; + tmp &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); + tmp |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; writel(tmp, nfc->regs + NFC_REG_ECC_CTL); @@ -783,10 +788,10 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, /* Fill OOB data in */ writel(NFC_BUF_TO_USER_DATA(oob), - nfc->regs + NFC_REG_USER_DATA_BASE); + nfc->regs + NFC_REG_USER_DATA(0)); tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | - (1 << 30); + NFC_ECC_OP; writel(tmp, nfc->regs + NFC_REG_CMD); ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); -- cgit v0.10.2 From 7af67226fb25fd68c9bf49adf7dd189dc2d58f87 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 20 May 2015 17:05:04 -0700 Subject: mtd: brcmnand: refactor bcm63138 SoC layering Removes an unnecessary allocation and saves a little bit of pointer chasing. Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/brcmnand/bcm63138_nand.c index 3f4c44c..59444b3 100644 --- a/drivers/mtd/nand/brcmnand/bcm63138_nand.c +++ b/drivers/mtd/nand/brcmnand/bcm63138_nand.c @@ -22,7 +22,8 @@ #include "brcmnand.h" -struct bcm63138_nand_soc_priv { +struct bcm63138_nand_soc { + struct brcmnand_soc soc; void __iomem *base; }; @@ -35,7 +36,8 @@ enum { static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc) { - struct bcm63138_nand_soc_priv *priv = soc->priv; + struct bcm63138_nand_soc *priv = + container_of(soc, struct bcm63138_nand_soc, soc); void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS; u32 val = brcmnand_readl(mmio); @@ -49,7 +51,8 @@ static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc) static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en) { - struct bcm63138_nand_soc_priv *priv = soc->priv; + struct bcm63138_nand_soc *priv = + container_of(soc, struct bcm63138_nand_soc, soc); void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN; u32 val = brcmnand_readl(mmio); @@ -64,25 +67,20 @@ static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en) static int bcm63138_nand_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct bcm63138_nand_soc_priv *priv; + struct bcm63138_nand_soc *priv; struct brcmnand_soc *soc; struct resource *res; - soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL); - if (!soc) - return -ENOMEM; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + soc = &priv->soc; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base"); priv->base = devm_ioremap_resource(dev, res); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - soc->pdev = pdev; - soc->priv = priv; soc->ctlrdy_ack = bcm63138_nand_intc_ack; soc->ctlrdy_set_enabled = bcm63138_nand_intc_set; -- cgit v0.10.2 From a86c947b25a57b2ff85132138d36ad75549b4cba Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 20 May 2015 17:05:05 -0700 Subject: mtd: brcmnand: refactor iProc SoC layering Removes an unnecessary allocation and saves a little bit of pointer chasing. Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/brcmnand/iproc_nand.c b/drivers/mtd/nand/brcmnand/iproc_nand.c index 683495c..585596c 100644 --- a/drivers/mtd/nand/brcmnand/iproc_nand.c +++ b/drivers/mtd/nand/brcmnand/iproc_nand.c @@ -22,7 +22,9 @@ #include "brcmnand.h" -struct iproc_nand_soc_priv { +struct iproc_nand_soc { + struct brcmnand_soc soc; + void __iomem *idm_base; void __iomem *ext_base; spinlock_t idm_lock; @@ -37,7 +39,8 @@ struct iproc_nand_soc_priv { static bool iproc_nand_intc_ack(struct brcmnand_soc *soc) { - struct iproc_nand_soc_priv *priv = soc->priv; + struct iproc_nand_soc *priv = + container_of(soc, struct iproc_nand_soc, soc); void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET; u32 val = brcmnand_readl(mmio); @@ -51,7 +54,8 @@ static bool iproc_nand_intc_ack(struct brcmnand_soc *soc) static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en) { - struct iproc_nand_soc_priv *priv = soc->priv; + struct iproc_nand_soc *priv = + container_of(soc, struct iproc_nand_soc, soc); void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; u32 val; unsigned long flags; @@ -72,7 +76,8 @@ static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en) static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare) { - struct iproc_nand_soc_priv *priv = soc->priv; + struct iproc_nand_soc *priv = + container_of(soc, struct iproc_nand_soc, soc); void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; u32 val; unsigned long flags; @@ -94,17 +99,14 @@ static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare) static int iproc_nand_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct iproc_nand_soc_priv *priv; + struct iproc_nand_soc *priv; struct brcmnand_soc *soc; struct resource *res; - soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL); - if (!soc) - return -ENOMEM; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + soc = &priv->soc; spin_lock_init(&priv->idm_lock); @@ -118,8 +120,6 @@ static int iproc_nand_probe(struct platform_device *pdev) if (IS_ERR(priv->ext_base)) return PTR_ERR(priv->ext_base); - soc->pdev = pdev; - soc->priv = priv; soc->ctlrdy_ack = iproc_nand_intc_ack; soc->ctlrdy_set_enabled = iproc_nand_intc_set; soc->prepare_data_bus = iproc_nand_apb_access; -- cgit v0.10.2 From 494da07c0f20245b3af54e33e1d2cd91d386c412 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 20 May 2015 17:05:06 -0700 Subject: mtd: brcmnand: remove unnecessary fields from brcmnand_soc These really aren't needed, especially now that we embed the soc struct in our private struct, so we can stash things there if needed. Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/brcmnand/brcmnand.h index 169f99e..ef5eabb 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.h +++ b/drivers/mtd/nand/brcmnand/brcmnand.h @@ -21,8 +21,6 @@ struct platform_device; struct dev_pm_ops; struct brcmnand_soc { - struct platform_device *pdev; - void *priv; bool (*ctlrdy_ack)(struct brcmnand_soc *soc); void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare); -- cgit v0.10.2 From 99a3b15572499e31bebc70d3d50bc13c0c49cbac Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 18 May 2015 16:24:30 -0700 Subject: sh: mach-rsk: remove unnecessary MTD partition probe specification The cmdlinepart parser is already supported in the default probe. Signed-off-by: Brian Norris diff --git a/arch/sh/boards/mach-rsk/setup.c b/arch/sh/boards/mach-rsk/setup.c index 2685ea0..6bc134b 100644 --- a/arch/sh/boards/mach-rsk/setup.c +++ b/arch/sh/boards/mach-rsk/setup.c @@ -27,8 +27,6 @@ static struct regulator_consumer_supply dummy_supplies[] = { REGULATOR_SUPPLY("vdd33a", "smsc911x"), }; -static const char *part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition rsk_partitions[] = { { .name = "Bootloader", @@ -50,7 +48,6 @@ static struct physmap_flash_data flash_data = { .parts = rsk_partitions, .nr_parts = ARRAY_SIZE(rsk_partitions), .width = 2, - .part_probe_types = part_probes, }; static struct resource flash_resource = { -- cgit v0.10.2 From a7f5ba40c74e67b2c8c6e15425157ab6775621d7 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 1 Oct 2015 16:58:27 +0200 Subject: mtd: nand: remove unused ->init_size() hook The ->init_size() hook was introduced to let NAND controller drivers support NAND devices that could not be described in the nand_ids table. Since then, the core has added support for extended-id parsing and full-id description, thus allowing to describe pretty much all existing NANDs. Moreover, this hook is not used by any mainline driver, and should not be used by new drivers, because detecting the NAND chip is not something controller specific. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 37c0d9d..a8230b1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3836,10 +3836,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->chipsize = (uint64_t)type->chipsize << 20; - if (!type->pagesize && chip->init_size) { - /* Set the pagesize, oobsize, erasesize by the driver */ - busw = chip->init_size(mtd, chip, id_data); - } else if (!type->pagesize) { + if (!type->pagesize) { /* Decode parameters from extended ID */ nand_decode_ext_id(mtd, chip, id_data, &busw); } else { diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index c4d8e30..50e1f94 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -556,10 +556,6 @@ struct nand_buffers { * @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 - * mtd->oobsize, mtd->writesize and so on. - * @id_data contains the 8 bytes values of NAND_CMD_READID. - * Return with the bus width. * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accessing * device ready/busy line. If set to NULL no access to * ready/busy is available and the ready/busy information @@ -658,8 +654,6 @@ struct nand_chip { int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); - int (*init_size)(struct mtd_info *mtd, struct nand_chip *this, - u8 *id_data); int (*dev_ready)(struct mtd_info *mtd); void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); -- cgit v0.10.2 From cbf29b83caa8c336cdb80b44bd1b959372e24d28 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 2 Oct 2015 12:40:20 +0200 Subject: mtd: nand: fsmc: Small whitespace cleanup Remove tab in empty line. Signed-off-by: Stefan Roese Cc: Linus Walleij Cc: Viresh Kumar Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 793872f..dc94882 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -960,7 +960,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->data_va = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->data_va)) return PTR_ERR(host->data_va); - + host->data_pa = (dma_addr_t)res->start; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); -- cgit v0.10.2 From 6efadcf9592c2ed438d1692ca55168e3de5c975b Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 2 Oct 2015 12:40:21 +0200 Subject: mtd: nand: fsmc: Remove BUG macros Remove the BUG macros and return with error (if possible) instead. Signed-off-by: Stefan Roese Cc: Linus Walleij Cc: Viresh Kumar Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index dc94882..45948e8 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -348,7 +348,7 @@ static void fsmc_select_chip(struct mtd_info *mtd, int chipnr) break; default: - BUG(); + dev_err(host->dev, "unsupported chip-select %d\n", chipnr); } } @@ -1111,7 +1111,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) default: dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", mtd->oobsize); - BUG(); + ret = -EINVAL; + goto err_probe; } } else { switch (host->mtd.oobsize) { @@ -1127,7 +1128,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) default: dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", mtd->oobsize); - BUG(); + ret = -EINVAL; + goto err_probe; } } -- cgit v0.10.2 From c9118ecebe1d694b463777ec33e397e226d4b149 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 30 Sep 2015 23:45:23 +0200 Subject: mtd: nand: sunxi: create sunxi_nfc_hw_ecc_enable()/disable() functions The code used to enable/disable the hardware ECC engine is repeated in a lot of places. Create two functions to avoid code duplication. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index dc44435..471fc2b 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -543,6 +543,30 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); } +static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_hw_ecc *data = nand->ecc.priv; + u32 ecc_ctl; + + ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | + NFC_ECC_BLOCK_SIZE_MSK); + ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; + + writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, + nfc->regs + NFC_REG_ECC_CTL); +} + static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) @@ -550,7 +574,6 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_ecclayout *layout = ecc->layout; - struct sunxi_nand_hw_ecc *data = ecc->priv; unsigned int max_bitflips = 0; int offset; int ret; @@ -558,11 +581,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, int i; int cnt; - tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); - tmp |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; - - writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { if (i) @@ -620,10 +639,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, } } - tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~NFC_ECC_EN; - - writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + sunxi_nfc_hw_ecc_disable(mtd); return max_bitflips; } @@ -635,18 +651,13 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_ecclayout *layout = ecc->layout; - struct sunxi_nand_hw_ecc *data = ecc->priv; int offset; int ret; u32 tmp; int i; int cnt; - tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); - tmp |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; - - writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { if (i) @@ -686,10 +697,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, } } - tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~NFC_ECC_EN; - - writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + sunxi_nfc_hw_ecc_disable(mtd); return 0; } @@ -701,7 +709,6 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); struct nand_ecc_ctrl *ecc = &chip->ecc; - struct sunxi_nand_hw_ecc *data = ecc->priv; unsigned int max_bitflips = 0; uint8_t *oob = chip->oob_poi; int offset = 0; @@ -710,11 +717,7 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, u32 tmp; int i; - tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); - tmp |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; - - writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { chip->read_buf(mtd, NULL, ecc->size); @@ -755,8 +758,7 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, } } - writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, - nfc->regs + NFC_REG_ECC_CTL); + sunxi_nfc_hw_ecc_disable(mtd); return max_bitflips; } @@ -768,7 +770,6 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, { struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); struct nand_ecc_ctrl *ecc = &chip->ecc; - struct sunxi_nand_hw_ecc *data = ecc->priv; uint8_t *oob = chip->oob_poi; int offset = 0; int ret; @@ -776,11 +777,7 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, u32 tmp; int i; - tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); - tmp |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; - - writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); @@ -810,10 +807,7 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, } } - tmp = readl(nfc->regs + NFC_REG_ECC_CTL); - tmp &= ~NFC_ECC_EN; - - writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + sunxi_nfc_hw_ecc_disable(mtd); return 0; } -- cgit v0.10.2 From 913821bdd2110b4569540c532ec50f3e61a7c9ba Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 30 Sep 2015 23:45:24 +0200 Subject: mtd: nand: sunxi: introduce sunxi_nfc_hw_ecc_read/write_chunk() The logic behind normal and syndrome ECC handling is pretty much the same, the only difference is the ECC bytes placement. Create two functions to read/write ECC chunks. Those functions will later be used by the sunxi_nfc_hw_ecc_read/write_page() and sunxi_nfc_hw_syndrome_ecc_read/write_page() functions. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 471fc2b..9d8cf06 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -567,6 +567,98 @@ static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd) nfc->regs + NFC_REG_ECC_CTL); } +static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, + u8 *data, int data_off, + u8 *oob, int oob_off, + int *cur_off, + unsigned int *max_bitflips) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + u32 status; + int ret; + + if (*cur_off != data_off) + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); + + sunxi_nfc_read_buf(mtd, data, ecc->size); + + if (data_off + ecc->bytes != oob_off) + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return ret; + + writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, + nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + if (ret) + return ret; + + status = readl(nfc->regs + NFC_REG_ECC_ST); + ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0))); + + memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); + + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + sunxi_nfc_read_buf(mtd, oob, ecc->bytes + 4); + + if (status & NFC_ECC_ERR(0)) + ret = -EIO; + + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + *max_bitflips = max_t(unsigned int, *max_bitflips, ret); + } + + *cur_off = oob_off + ecc->bytes + 4; + + return 0; +} + +static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, + const u8 *data, int data_off, + const u8 *oob, int oob_off, + int *cur_off) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + int ret; + + if (data_off != *cur_off) + nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1); + + sunxi_nfc_write_buf(mtd, data, ecc->size); + + /* Fill OOB data in */ + writel(NFC_BUF_TO_USER_DATA(oob), nfc->regs + NFC_REG_USER_DATA(0)); + + if (data_off + ecc->bytes != oob_off) + nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return ret; + + writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | + NFC_ACCESS_DIR | NFC_ECC_OP, + nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + if (ret) + return ret; + + *cur_off = oob_off + ecc->bytes + 4; + + return 0; +} + static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) -- cgit v0.10.2 From b462551c125a125d1782b53d4c7ee7fd35534641 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 30 Sep 2015 23:45:25 +0200 Subject: mtd: nand: sunxi: make use of sunxi_nfc_hw_ecc_read/write_chunk() The sunxi_nfc_hw_ecc_read/write_chunk() functions have been created to factorize the code in the normal and syndrome ECC implementation. Make use of them where appropriate. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 9d8cf06..176d0f0 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -663,61 +663,25 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); struct nand_ecc_ctrl *ecc = &chip->ecc; - struct nand_ecclayout *layout = ecc->layout; unsigned int max_bitflips = 0; + int ret, i, cur_off = 0; int offset; - int ret; - u32 tmp; - int i; int cnt; sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { - if (i) - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1); - - offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4; - - chip->read_buf(mtd, NULL, ecc->size); - - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + u8 *data = buf + data_off; + u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, + oob_off + mtd->writesize, + &cur_off, &max_bitflips); if (ret) return ret; - - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP; - writel(tmp, nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); - if (ret) - return ret; - - memcpy_fromio(buf + (i * ecc->size), - nfc->regs + NFC_RAM0_BASE, ecc->size); - - if (readl(nfc->regs + NFC_REG_ECC_ST) & NFC_ECC_ERR(0)) { - mtd->ecc_stats.failed++; - } else { - tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)); - mtd->ecc_stats.corrected += NFC_ECC_ERR_CNT(0, tmp); - max_bitflips = max_t(unsigned int, max_bitflips, tmp); - } - - if (oob_required) { - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return ret; - - offset -= mtd->writesize; - chip->read_buf(mtd, chip->oob_poi + offset, - ecc->bytes + 4); - } } if (oob_required) { @@ -740,40 +704,22 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); struct nand_ecc_ctrl *ecc = &chip->ecc; - struct nand_ecclayout *layout = ecc->layout; + int ret, i, cur_off = 0; int offset; - int ret; - u32 tmp; - int i; int cnt; sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { - if (i) - chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1); - - chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); - - offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize; - - /* Fill OOB data in */ - writel(NFC_BUF_TO_USER_DATA(chip->oob_poi + - layout->oobfree[i].offset), - nfc->regs + NFC_REG_USER_DATA(0)); - - chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return ret; - - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | - NFC_ECC_OP; - writel(tmp, nfc->regs + NFC_REG_CMD); - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + const u8 *data = buf + data_off; + const u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, + oob_off + mtd->writesize, + &cur_off); if (ret) return ret; } @@ -799,53 +745,31 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, uint8_t *buf, int oob_required, int page) { - struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0; - uint8_t *oob = chip->oob_poi; - int offset = 0; - int ret; + int ret, i, cur_off = 0; int cnt; - u32 tmp; - int i; sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { - chip->read_buf(mtd, NULL, ecc->size); - - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP; - writel(tmp, nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + int data_off = i * (ecc->size + ecc->bytes + 4); + int oob_off = data_off + ecc->size; + u8 *data = buf + (i * ecc->size); + u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); + + ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, + oob_off, &cur_off, + &max_bitflips); if (ret) return ret; - - memcpy_fromio(buf, nfc->regs + NFC_RAM0_BASE, ecc->size); - buf += ecc->size; - offset += ecc->size; - - if (readl(nfc->regs + NFC_REG_ECC_ST) & NFC_ECC_ERR(0)) { - mtd->ecc_stats.failed++; - } else { - tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)); - mtd->ecc_stats.corrected += NFC_ECC_ERR_CNT(0, tmp); - max_bitflips = max_t(unsigned int, max_bitflips, tmp); - } - - if (oob_required) { - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); - chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad); - oob += ecc->bytes + ecc->prepad; - } - - offset += ecc->bytes + ecc->prepad; } if (oob_required) { - cnt = mtd->oobsize - (oob - chip->oob_poi); + cnt = mtd->writesize + mtd->oobsize - cur_off; if (cnt > 0) { - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + u8 *oob = chip->oob_poi + mtd->oobsize - cnt; + chip->read_buf(mtd, oob, cnt); } } @@ -860,41 +784,29 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, const uint8_t *buf, int oob_required) { - struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); struct nand_ecc_ctrl *ecc = &chip->ecc; - uint8_t *oob = chip->oob_poi; - int offset = 0; - int ret; + int ret, i, cur_off = 0; int cnt; - u32 tmp; - int i; sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { - chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); - offset += ecc->size; - - /* Fill OOB data in */ - writel(NFC_BUF_TO_USER_DATA(oob), - nfc->regs + NFC_REG_USER_DATA(0)); - - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | - NFC_ECC_OP; - writel(tmp, nfc->regs + NFC_REG_CMD); + int data_off = i * (ecc->size + ecc->bytes + 4); + int oob_off = data_off + ecc->size; + const u8 *data = buf + (i * ecc->size); + const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, + oob, oob_off, &cur_off); if (ret) return ret; - - offset += ecc->bytes + ecc->prepad; - oob += ecc->bytes + ecc->prepad; } if (oob_required) { - cnt = mtd->oobsize - (oob - chip->oob_poi); + cnt = mtd->writesize + mtd->oobsize - cur_off; if (cnt > 0) { - chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); + u8 *oob = chip->oob_poi + mtd->oobsize - cnt; + chip->write_buf(mtd, oob, cnt); } } -- cgit v0.10.2 From 35d0e24f09d00e27eae3484784feee24effc23c5 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 30 Sep 2015 23:45:26 +0200 Subject: mtd: nand: sunxi: factorize extra OOB bytes handling Add helper functions to factorize the code dealing extra OOB bytes in the normal and syndrome ECC implementations. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 176d0f0..45b74fc 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -621,6 +621,26 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, return 0; } +static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, + u8 *oob, int *cur_off) +{ + struct nand_chip *nand = mtd->priv; + struct nand_ecc_ctrl *ecc = &nand->ecc; + int offset = ((ecc->bytes + 4) * ecc->steps); + int len = mtd->oobsize - offset; + + if (len <= 0) + return; + + if (*cur_off != offset) + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, + offset + mtd->writesize, -1); + + sunxi_nfc_read_buf(mtd, oob + offset, len); + + *cur_off = mtd->oobsize + mtd->writesize; +} + static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, const u8 *data, int data_off, const u8 *oob, int oob_off, @@ -659,6 +679,26 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, return 0; } +static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, + u8 *oob, int *cur_off) +{ + struct nand_chip *nand = mtd->priv; + struct nand_ecc_ctrl *ecc = &nand->ecc; + int offset = ((ecc->bytes + 4) * ecc->steps); + int len = mtd->oobsize - offset; + + if (len <= 0) + return; + + if (*cur_off != offset) + nand->cmdfunc(mtd, NAND_CMD_RNDIN, + offset + mtd->writesize, -1); + + sunxi_nfc_write_buf(mtd, oob + offset, len); + + *cur_off = mtd->oobsize + mtd->writesize; +} + static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) @@ -666,8 +706,6 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0; int ret, i, cur_off = 0; - int offset; - int cnt; sunxi_nfc_hw_ecc_enable(mtd); @@ -684,16 +722,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, return ret; } - if (oob_required) { - cnt = ecc->layout->oobfree[ecc->steps].length; - if (cnt > 0) { - offset = mtd->writesize + - ecc->layout->oobfree[ecc->steps].offset; - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); - offset -= mtd->writesize; - chip->read_buf(mtd, chip->oob_poi + offset, cnt); - } - } + if (oob_required) + sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off); sunxi_nfc_hw_ecc_disable(mtd); @@ -706,8 +736,6 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, { struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; - int offset; - int cnt; sunxi_nfc_hw_ecc_enable(mtd); @@ -724,16 +752,8 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, return ret; } - if (oob_required) { - cnt = ecc->layout->oobfree[i].length; - if (cnt > 0) { - offset = mtd->writesize + - ecc->layout->oobfree[i].offset; - chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); - offset -= mtd->writesize; - chip->write_buf(mtd, chip->oob_poi + offset, cnt); - } - } + if (oob_required) + sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off); sunxi_nfc_hw_ecc_disable(mtd); @@ -748,7 +768,6 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0; int ret, i, cur_off = 0; - int cnt; sunxi_nfc_hw_ecc_enable(mtd); @@ -765,14 +784,8 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, return ret; } - if (oob_required) { - cnt = mtd->writesize + mtd->oobsize - cur_off; - if (cnt > 0) { - u8 *oob = chip->oob_poi + mtd->oobsize - cnt; - - chip->read_buf(mtd, oob, cnt); - } - } + if (oob_required) + sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off); sunxi_nfc_hw_ecc_disable(mtd); @@ -786,7 +799,6 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, { struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; - int cnt; sunxi_nfc_hw_ecc_enable(mtd); @@ -802,14 +814,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, return ret; } - if (oob_required) { - cnt = mtd->writesize + mtd->oobsize - cur_off; - if (cnt > 0) { - u8 *oob = chip->oob_poi + mtd->oobsize - cnt; - - chip->write_buf(mtd, oob, cnt); - } - } + if (oob_required) + sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off); sunxi_nfc_hw_ecc_disable(mtd); -- cgit v0.10.2 From f363e0faa8bd5adb00739086db75713c669b4d9e Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 30 Sep 2015 23:45:27 +0200 Subject: mtd: nand: sunxi: retrieve corrected OOB bytes The ECC engine is protecting a few OOB bytes. Retrieve them from the USER_DATA register instead of reading them in raw mode (ie without the ECC protection). Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 45b74fc..a9061a0 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -567,6 +567,14 @@ static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd) nfc->regs + NFC_REG_ECC_CTL); } +static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) +{ + buf[0] = user_data; + buf[1] = user_data >> 8; + buf[2] = user_data >> 16; + buf[3] = user_data >> 24; +} + static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, u8 *data, int data_off, u8 *oob, int oob_off, @@ -606,8 +614,16 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); sunxi_nfc_read_buf(mtd, oob, ecc->bytes + 4); - if (status & NFC_ECC_ERR(0)) + if (status & NFC_ECC_ERR(0)) { ret = -EIO; + } else { + /* + * The engine protects 4 bytes of OOB data per chunk. + * Retrieve the corrected OOB bytes. + */ + sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)), + oob); + } if (ret < 0) { mtd->ecc_stats.failed++; -- cgit v0.10.2 From 23151fd6138a3959628ea093237ca3f9d597e155 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 30 Sep 2015 23:45:28 +0200 Subject: mtd: nand: sunxi: replace the NFC_BUF_TO_USER_DATA() macro by an inline function sunxi_nfc_user_data_to_buf() is exposed as an inline function, replace the NFC_BUF_TO_USER_DATA() macro by an inline function to be consistent. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index a9061a0..a76eb51 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -156,10 +156,6 @@ #define NFC_ECC_PAT_FOUND(x) BIT(x + 16) #define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff) -/* NFC_USER_DATA helper macros */ -#define NFC_BUF_TO_USER_DATA(buf) ((buf)[0] | ((buf)[1] << 8) | \ - ((buf)[2] << 16) | ((buf)[3] << 24)) - #define NFC_DEFAULT_TIMEOUT_MS 1000 #define NFC_SRAM_SIZE 1024 @@ -657,6 +653,11 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, *cur_off = mtd->oobsize + mtd->writesize; } +static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) +{ + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, const u8 *data, int data_off, const u8 *oob, int oob_off, @@ -673,7 +674,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, sunxi_nfc_write_buf(mtd, data, ecc->size); /* Fill OOB data in */ - writel(NFC_BUF_TO_USER_DATA(oob), nfc->regs + NFC_REG_USER_DATA(0)); + writel(sunxi_nfc_buf_to_user_data(oob), + nfc->regs + NFC_REG_USER_DATA(0)); if (data_off + ecc->bytes != oob_off) nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); -- cgit v0.10.2 From 146b503e102778ee98d8ba59e9e178ab26a4af5b Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 30 Sep 2015 23:45:29 +0200 Subject: mtd: nand: sunxi: fix bitflips in erased pages Use the nand_check_erased_ecc_chunk() function to test if the ECC error was triggered by an erased page containing a few bitflips. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index a76eb51..92245a3 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -611,7 +611,9 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, sunxi_nfc_read_buf(mtd, oob, ecc->bytes + 4); if (status & NFC_ECC_ERR(0)) { - ret = -EIO; + ret = nand_check_erased_ecc_chunk(data, ecc->size, + oob, ecc->bytes + 4, + NULL, 0, ecc->strength); } else { /* * The engine protects 4 bytes of OOB data per chunk. -- cgit v0.10.2 From 641f6342f507cfe671ac5a58768a8473e14ae2ac Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 1 Oct 2015 02:23:35 +0300 Subject: mtd: nand: lpc32xx_slc: improve SLCTAC_*() macro definitions No functional change, move bitfield calculations to macro definitions with added clock rate argument, which are in turn defined by new common SLCTAC_CLOCKS(c, n, s) macro definition. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index abfec13..9ac0f3b 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -94,22 +94,25 @@ /********************************************************************** * slc_tac register definitions **********************************************************************/ +/* Computation of clock cycles on basis of controller and device clock rates */ +#define SLCTAC_CLOCKS(c, n, s) (((1 + (c / n)) & 0xF) << s) + /* Clock setting for RDY write sample wait time in 2*n clocks */ #define SLCTAC_WDR(n) (((n) & 0xF) << 28) /* Write pulse width in clock cycles, 1 to 16 clocks */ -#define SLCTAC_WWIDTH(n) (((n) & 0xF) << 24) +#define SLCTAC_WWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 24)) /* Write hold time of control and data signals, 1 to 16 clocks */ -#define SLCTAC_WHOLD(n) (((n) & 0xF) << 20) +#define SLCTAC_WHOLD(c, n) (SLCTAC_CLOCKS(c, n, 20)) /* Write setup time of control and data signals, 1 to 16 clocks */ -#define SLCTAC_WSETUP(n) (((n) & 0xF) << 16) +#define SLCTAC_WSETUP(c, n) (SLCTAC_CLOCKS(c, n, 16)) /* Clock setting for RDY read sample wait time in 2*n clocks */ #define SLCTAC_RDR(n) (((n) & 0xF) << 12) /* Read pulse width in clock cycles, 1 to 16 clocks */ -#define SLCTAC_RWIDTH(n) (((n) & 0xF) << 8) +#define SLCTAC_RWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 8)) /* Read hold time of control and data signals, 1 to 16 clocks */ -#define SLCTAC_RHOLD(n) (((n) & 0xF) << 4) +#define SLCTAC_RHOLD(c, n) (SLCTAC_CLOCKS(c, n, 4)) /* Read setup time of control and data signals, 1 to 16 clocks */ -#define SLCTAC_RSETUP(n) (((n) & 0xF) << 0) +#define SLCTAC_RSETUP(c, n) (SLCTAC_CLOCKS(c, n, 0)) /********************************************************************** * slc_ecc register definitions @@ -240,13 +243,13 @@ static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) /* Compute clock setup values */ tmp = SLCTAC_WDR(host->ncfg->wdr_clks) | - SLCTAC_WWIDTH(1 + (clkrate / host->ncfg->wwidth)) | - SLCTAC_WHOLD(1 + (clkrate / host->ncfg->whold)) | - SLCTAC_WSETUP(1 + (clkrate / host->ncfg->wsetup)) | + SLCTAC_WWIDTH(clkrate, host->ncfg->wwidth) | + SLCTAC_WHOLD(clkrate, host->ncfg->whold) | + SLCTAC_WSETUP(clkrate, host->ncfg->wsetup) | SLCTAC_RDR(host->ncfg->rdr_clks) | - SLCTAC_RWIDTH(1 + (clkrate / host->ncfg->rwidth)) | - SLCTAC_RHOLD(1 + (clkrate / host->ncfg->rhold)) | - SLCTAC_RSETUP(1 + (clkrate / host->ncfg->rsetup)); + SLCTAC_RWIDTH(clkrate, host->ncfg->rwidth) | + SLCTAC_RHOLD(clkrate, host->ncfg->rhold) | + SLCTAC_RSETUP(clkrate, host->ncfg->rsetup); writel(tmp, SLC_TAC(host->io_base)); } -- cgit v0.10.2 From 08d3cd5ef0633df84d119e939d8d1b56c6e4a5e7 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 1 Oct 2015 02:23:36 +0300 Subject: mtd: nand: lpc32xx_slc: fix potential overflow over 4 bits In case if quotient of controller clock rate to device clock rate does not fit into 4 bit value, choose the maximum acceptable value 0xF, which stands for 16 clocks. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index 9ac0f3b..a9e8a02 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -95,7 +95,7 @@ * slc_tac register definitions **********************************************************************/ /* Computation of clock cycles on basis of controller and device clock rates */ -#define SLCTAC_CLOCKS(c, n, s) (((1 + (c / n)) & 0xF) << s) +#define SLCTAC_CLOCKS(c, n, s) (min_t(u32, 1 + (c / n), 0xF) << s) /* Clock setting for RDY write sample wait time in 2*n clocks */ #define SLCTAC_WDR(n) (((n) & 0xF) << 28) -- cgit v0.10.2 From d54e88011d0a5fb48d9bb60fede3e83375c75841 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 1 Oct 2015 02:23:37 +0300 Subject: mtd: nand: lpc32xx_slc: fix calculation of timing arcs from given values According to LPC32xx User's Manual all values measured in clock cycles are programmable from 1 to 16 clocks (4 bits) starting from 0 in bitfield, the current version of calculated clock cycles is too conservative. Correctness of 0 bitfield value (i.e. programmed 1 clock timing) is proven with actual NAND chip devices. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index a9e8a02..cbf4501 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -95,7 +95,7 @@ * slc_tac register definitions **********************************************************************/ /* Computation of clock cycles on basis of controller and device clock rates */ -#define SLCTAC_CLOCKS(c, n, s) (min_t(u32, 1 + (c / n), 0xF) << s) +#define SLCTAC_CLOCKS(c, n, s) (min_t(u32, DIV_ROUND_UP(c, n) - 1, 0xF) << s) /* Clock setting for RDY write sample wait time in 2*n clocks */ #define SLCTAC_WDR(n) (((n) & 0xF) << 28) -- cgit v0.10.2 From eb042ec35956de2684de2a05a814cd15efe570ca Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Tue, 29 Sep 2015 11:23:55 +0800 Subject: jffs2: fix a memleak in read_direntry() Need to free the memory allocated for 'fd' if failed to read all of the remainder name. Signed-off-by: Wei Fang Signed-off-by: Brian Norris diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 28e0aab..bfebbf1 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -660,8 +660,12 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r err = jffs2_flash_read(c, (ref_offset(ref)) + read, rd->nsize - already, &read, &fd->name[already]); - if (unlikely(read != rd->nsize - already) && likely(!err)) + if (unlikely(read != rd->nsize - already) && likely(!err)) { + jffs2_free_full_dirent(fd); + JFFS2_ERROR("short read: wanted %d bytes, got %zd\n", + rd->nsize - already, read); return -EIO; + } if (unlikely(err)) { JFFS2_ERROR("read remainder of name: error %d\n", err); -- cgit v0.10.2 From 8e2c992b59fcb5e56e3667f5c30c7d26fbbf14a2 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Tue, 18 Aug 2015 15:34:07 +0000 Subject: mtd: mtdpart: add debug prints to partition parser. The probe of a mtd device can fail when a partition parser returns error. The failure due to partition parsing can be quite mysterious when multiple partitioning schemes are compiled in and any of them can fail the probe. Add debug prints which show what parsers were tried and what they returned. Signed-off-by: Michal Suchanek Signed-off-by: Brian Norris diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 919a936..f5279ea 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -761,12 +761,17 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, types = default_mtd_part_types; for ( ; ret <= 0 && *types; types++) { + pr_debug("%s: parsing partitions %s\n", master->name, *types); parser = get_partition_parser(*types); if (!parser && !request_module("%s", *types)) parser = get_partition_parser(*types); + pr_debug("%s: got parser %s\n", master->name, + parser ? parser->name : NULL); if (!parser) continue; ret = (*parser->parse_fn)(master, pparts, data); + pr_debug("%s: parser %s: %i\n", + master->name, parser->name, ret); put_partition_parser(parser); if (ret > 0) { printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", -- cgit v0.10.2 From 192db1caa253c087496b9671ab8aa0a53da31f35 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Fri, 31 Jul 2015 01:12:44 +0000 Subject: mtd: nand_bbt: set the smallest size of bbt table When using nandsim to simulate a 128K block nand with `overridesize = 1', the size of mtd device is too small (mtd_size = 4 * block_size) to get the right length of bbt. Then when creating bbt, kzmalloc() will return ZERO_SIZE_PTR. This causes a NULL pointer oops when scanning bbt. [ 952.156166] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010 [ 952.157064] IP: [] nand_isreserved_bbt+0x2a/0x40 [ 952.157064] PGD 0 [ 952.157064] Oops: 0000 [#1] SMP [ 952.157064] Modules linked in: nandsim(+) [last unloaded: nandsim] [ 952.157064] CPU: 1 PID: 7103 Comm: modprobe Not tainted 4.2.0-rc3-next-20150724 #4 [ 952.157064] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 952.157064] task: ffff88003e24b980 ti: ffff88003d274000 task.ti: ffff88003d274000 [ 952.157064] RIP: 0010:[] [] nand_isreserved_bbt+0x2a/0x40 [ 952.157064] RSP: 0018:ffff88003d277b90 EFLAGS: 00010246 [ 952.157064] RAX: 0000000000000010 RBX: ffff88003d5a1000 RCX: 0000000000000000 [ 952.157064] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88003d919000 [ 952.157064] RBP: ffff88003d277b98 R08: 0000000000020000 R09: 0000000000000000 [ 952.157064] R10: 0000000000000000 R11: 0000000000000195 R12: ffff88003d919000 [ 952.157064] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 [ 952.157064] FS: 00007fada4d07700(0000) GS:ffff88003fd00000(0000) knlGS:0000000000000000 [ 952.157064] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 952.157064] CR2: 0000000000000010 CR3: 0000000037924000 CR4: 00000000000006a0 [ 952.157064] Stack: [ 952.157064] ffffffff814851ec ffff88003d277ba8 ffffffff8147e35f ffff88003d277bf8 [ 952.157064] ffffffff814816f3 ffff88003d277c08 ffff88003d277bc8 0000000000000282 [ 952.157064] 0000000000000001 0000000000000000 ffff88003d209540 0000000000000001 [ 952.157064] Call Trace: [ 952.157064] [] ? nand_block_isreserved+0x1c/0x20 [ 952.157064] [] mtd_block_isreserved+0x1f/0x30 [ 952.157064] [] allocate_partition+0x463/0x6a0 [ 952.157064] [] add_mtd_partitions+0x4b/0xe0 [ 952.157064] [] mtd_device_parse_register+0x4c/0xe0 [ 952.157064] [] ns_init_module+0xdaf/0xde4 [nandsim] [ 952.157064] [] ? kasprintf+0x38/0x40 [ 952.157064] [] ? 0xffffffffa0013000 [ 952.157064] [] do_one_initcall+0x83/0x1b0 [ 952.157064] [] ? kmem_cache_alloc_trace+0x6b/0x120 [ 952.157064] [] do_init_module+0x5c/0x1dd [ 952.157064] [] load_module+0x1bbb/0x20b0 [ 952.157064] [] ? __symbol_put+0x30/0x30 [ 952.157064] [] SyS_init_module+0xf9/0x110 [ 952.157064] [] ? SyS_init_module+0x1/0x110 [ 952.157064] [] entry_SYSCALL_64_fastpath+0x12/0x6a [ 952.157064] Code: 00 55 48 8b 87 80 01 00 00 48 89 e5 8b 88 cc 00 00 00 48 8b 80 f0 03 00 00 5d 48 d3 fe 89 f2 83 e6 03 c1 fa 02 8d 0c 36 48 63 d2 <0f> b6 04 10 d3 f8 83 e0 03 3c 02 0f 94 c0 0f b6 c0 c3 0f 1f 40 [ 952.157064] RIP [] nand_isreserved_bbt+0x2a/0x40 [ 952.157064] RSP [ 952.157064] CR2: 0000000000000010 [ 952.204010] ---[ end trace 6ca2e1c041fdba36 ]--- This patch gives a smallest length to bbt, 1 byte, which is enough to represent up to 4 blocks. Signed-off-by: Sheng Yong Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 63a1a36..b1d4f81 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1080,7 +1080,7 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) struct nand_bbt_descr *td = this->bbt_td; struct nand_bbt_descr *md = this->bbt_md; - len = mtd->size >> (this->bbt_erase_shift + 2); + len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1; /* * Allocate memory (2bit per block) and clear the memory bad block * table. -- cgit v0.10.2 From 06968a54790d9dd87a5bb68e8197b5094eff63c3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 9 Oct 2015 18:16:51 -0400 Subject: mtd: pxa2xx-flash: switch from ioremap_cache to memremap In preparation for deprecating ioremap_cache() convert its usage in pxa2xx-flash to memremap. Cc: David Woodhouse [brian: also convert iounmap to memunmap] Signed-off-by: Dan Williams Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 12fa75d..5ff6cc5 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -71,8 +71,8 @@ static int pxa2xx_flash_probe(struct platform_device *pdev) info->map.name); return -ENOMEM; } - info->map.cached = - ioremap_cache(info->map.phys, info->map.size); + info->map.cached = memremap(info->map.phys, info->map.size, + MEMREMAP_WB); if (!info->map.cached) printk(KERN_WARNING "Failed to ioremap cached %s\n", info->map.name); @@ -111,7 +111,7 @@ static int pxa2xx_flash_remove(struct platform_device *dev) map_destroy(info->mtd); iounmap(info->map.virt); if (info->map.cached) - iounmap(info->map.cached); + memunmap(info->map.cached); kfree(info); return 0; } -- cgit v0.10.2 From 3f08b8ba9f5d7ec528a9fc293e76a14c2851e403 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 2 Oct 2015 23:26:42 +0530 Subject: mtd: brcmnand: Fix pointer type-cast in brcmnand_write() We should always type-cast pointer to "long" or "unsigned long" because size of pointer is same as machine word size. This will avoid pointer type-cast issues on both 32bit and 64bit systems. This patch fixes pointer type-cast issue in brcmnand_write() as-per above info. Signed-off-by: Anup Patel Reviewed-by: Vikram Prakash Reviewed-by: Ray Jui Reviewed-by: Scott Branden Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index 048e4e0..ea319a4 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1544,9 +1544,9 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf); - if (unlikely((u32)buf & 0x03)) { + if (unlikely((unsigned long)buf & 0x03)) { dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf); - buf = (u32 *)((u32)buf & ~0x03); + buf = (u32 *)((unsigned long)buf & ~0x03); } brcmnand_wp(mtd, 0); -- cgit v0.10.2 From ebdee13ac2b4f1463c1c195a40fa4f1555184f17 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 2 Oct 2015 23:26:43 +0530 Subject: mtd: nand: Allow MTD_NAND_BRCMNAND to be selected for ARM64 The BRCM NAND driver can be re-used for Broadcom ARM64 SoCs hence this patch updates Kconfig to allow selection of MTD_NAND_BRCMNAND for ARM64. Signed-off-by: Anup Patel Reviewed-by: Vikram Prakash Reviewed-by: Ray Jui Reviewed-by: Pramod KUMAR Reviewed-by: Scott Branden Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ccd1158..2896640 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -393,7 +393,7 @@ config MTD_NAND_GPMI_NAND config MTD_NAND_BRCMNAND tristate "Broadcom STB NAND controller" - depends on ARM || MIPS + depends on ARM || ARM64 || MIPS help Enables the Broadcom NAND controller driver. The controller was originally designed for Set-Top Box but is used on various BCM7xxx, -- cgit v0.10.2 From f9bcb6dc8013d4da6660556ddf2383e4fbcbe3d7 Mon Sep 17 00:00:00 2001 From: Aurelien Chanot Date: Wed, 7 Oct 2015 12:10:08 -0700 Subject: mtd: spi-nor: Add support for Micron n25q032a The N25Q032A is identical to the N25Q032 except it has a different supply voltage range. Therefore, it has a new JEDEC ID. Signed-off-by: Aurelien Chanot Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 4be41fb..0397dba 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -615,6 +615,7 @@ static const struct flash_info spi_nor_ids[] = { /* Micron */ { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, + { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, -- cgit v0.10.2 From 260e89a6e0d6dcaccd484cf13a69285c3d22268f Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:15 +0200 Subject: mtd: core: tone down suggestion that dev.parent should be set add_mtd_device() has a comment suggesting that the caller should have set dev.parent. This is required to have the parent device symlink show up in sysfs, but not for proper operation of the mtd device itself. Currently we have five drivers registering mtd devices during module initialization, so they don't actually provide a parent device to link to. That means we cannot WARN_ON() here, as it would trigger false positives. Make the comment a bit less firm in its assertion that dev.parent should be set. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 8598e3b..ac280ee 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -430,7 +430,7 @@ int add_mtd_device(struct mtd_info *mtd) } /* Caller should have set dev.parent to match the - * physical device. + * physical device, if appropriate. */ mtd->dev.type = &mtd_devtype; mtd->dev.class = &mtd_class; -- cgit v0.10.2 From 807f16d4db956b364ae852a63ad7d79460838866 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:16 +0200 Subject: mtd: core: set some defaults when dev.parent is set If a parent device is set, add_mtd_device() has enough knowledge to fill in some sane default values for the module name and owner. Do so if they aren't already set. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index ac280ee..bbcba0d 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -418,6 +418,15 @@ int add_mtd_device(struct mtd_info *mtd) mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; + if (mtd->dev.parent) { + if (!mtd->owner && mtd->dev.parent->driver) + mtd->owner = mtd->dev.parent->driver->owner; + if (!mtd->name) + mtd->name = dev_name(mtd->dev.parent); + } else { + pr_debug("mtd device won't show a device symlink in sysfs\n"); + } + /* Some chips always power up locked. Unlock them now */ if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) { error = mtd_unlock(mtd, 0, mtd->size); -- cgit v0.10.2 From eb98198f23d0c6a77afe692b25e8ad634f0404c5 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:17 +0200 Subject: mtd: devices: bcm47xxflash: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 3d008a9..347bb83 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -237,13 +237,14 @@ static int bcm47xxsflash_write(struct mtd_info *mtd, loff_t to, size_t len, return 0; } -static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) +static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s, + struct device *dev) { struct mtd_info *mtd = &b47s->mtd; mtd->priv = b47s; + mtd->dev.parent = dev; mtd->name = "bcm47xxsflash"; - mtd->owner = THIS_MODULE; mtd->type = MTD_NORFLASH; mtd->flags = MTD_CAP_NORFLASH; @@ -300,7 +301,7 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) b47s->blocksize = sflash->blocksize; b47s->numblocks = sflash->numblocks; b47s->size = sflash->size; - bcm47xxsflash_fill_mtd(b47s); + bcm47xxsflash_fill_mtd(b47s, &pdev->dev); err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); if (err) { -- cgit v0.10.2 From 1560d2132ae95b36b25cc78811af8eaf9bbb2f8e Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:18 +0200 Subject: mtd: devices: docg3: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 5e67b4a..f00d0da 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1843,7 +1843,6 @@ static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) mtd->erasesize /= 2; mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE; mtd->oobsize = DOC_LAYOUT_OOB_SIZE; - mtd->owner = THIS_MODULE; mtd->_erase = doc_erase; mtd->_read = doc_read; mtd->_write = doc_write; @@ -1885,6 +1884,7 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev) if (!mtd) goto nomem2; mtd->priv = docg3; + mtd->dev.parent = dev; bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, 8 * DOC_LAYOUT_PAGE_SIZE); docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL); -- cgit v0.10.2 From 57eea0f5fb221f88c3c9b0ddc967c004bfff7e9c Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:20 +0200 Subject: mtd: devices: mtd_dataflash: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index df6f611..39666d5 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -648,7 +648,6 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, device->size = nr_pages * pagesize; device->erasesize = pagesize; device->writesize = pagesize; - device->owner = THIS_MODULE; device->type = MTD_DATAFLASH; device->flags = MTD_WRITEABLE; device->_erase = dataflash_erase; -- cgit v0.10.2 From 7d24272253594abcdca8f52758a574367a5912bf Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:21 +0200 Subject: mtd: devices: spear_smi: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 04b24d2..64c7458 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -854,6 +854,7 @@ static int spear_smi_setup_banks(struct platform_device *pdev, else flash->mtd.name = flash_devices[flash_index].name; + flash->mtd.dev.parent = &pdev->dev; flash->mtd.type = MTD_NORFLASH; flash->mtd.writesize = 1; flash->mtd.flags = MTD_CAP_NORFLASH; -- cgit v0.10.2 From 90b997527ecbaac44485a52a01768d5ae32f3818 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:22 +0200 Subject: mtd: devices: sst251: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index c63ecbc..3928014 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -374,9 +374,8 @@ static int sst25l_probe(struct spi_device *spi) data = dev_get_platdata(&spi->dev); if (data && data->name) flash->mtd.name = data->name; - else - flash->mtd.name = dev_name(&spi->dev); + flash->mtd.dev.parent = &spi->dev; flash->mtd.type = MTD_NORFLASH; flash->mtd.flags = MTD_CAP_NORFLASH; flash->mtd.erasesize = flash_info->erase_size; -- cgit v0.10.2 From 3aed61d1eb06b8b19b7bb09d49b222ebc3f83347 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:23 +0200 Subject: mtd: lpddr: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/lpddr/lpddr2_nvm.c b/drivers/mtd/lpddr/lpddr2_nvm.c index 063cec4..2342277 100644 --- a/drivers/mtd/lpddr/lpddr2_nvm.c +++ b/drivers/mtd/lpddr/lpddr2_nvm.c @@ -460,6 +460,7 @@ static int lpddr2_nvm_probe(struct platform_device *pdev) /* Populate mtd_info data structure */ *mtd = (struct mtd_info) { + .dev = { .parent = &pdev->dev }, .name = pdev->dev.init_name, .type = MTD_RAM, .priv = map, -- cgit v0.10.2 From 28bc7406bd6d0e3fb50f29348bafcaa9e34fabf9 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:25 +0200 Subject: mtd: maps: gpio-addr-flash: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c index 2fb3460..385305e 100644 --- a/drivers/mtd/maps/gpio-addr-flash.c +++ b/drivers/mtd/maps/gpio-addr-flash.c @@ -266,7 +266,7 @@ static int gpio_flash_probe(struct platform_device *pdev) kfree(state); return -ENXIO; } - + state->mtd->dev.parent = &pdev->dev; mtd_device_parse_register(state->mtd, part_probe_types, NULL, pdata->parts, pdata->nr_parts); -- cgit v0.10.2 From 5e50a52eb954ca6c23b1d935b77ba75ec125352e Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:26 +0200 Subject: mtd: maps: intel_vr_nor: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c index 5ab71f0..8bf7977 100644 --- a/drivers/mtd/maps/intel_vr_nor.c +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -90,7 +90,7 @@ static int vr_nor_mtd_setup(struct vr_nor_mtd *p) if (!p->info) return -ENODEV; - p->info->owner = THIS_MODULE; + p->info->dev.parent = &p->dev->dev; return 0; } -- cgit v0.10.2 From e4e07db4ce50d97f14ed1f9bb10c3782cce5fe26 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:27 +0200 Subject: mtd: maps: ixp4xx: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index b443074..e3180d5 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -226,7 +226,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev) err = -ENXIO; goto Error; } - info->mtd->owner = THIS_MODULE; + info->mtd->dev.parent = &dev->dev; /* Use the fast version */ info->map.write = ixp4xx_write16; -- cgit v0.10.2 From c54c2fb783e2ae6636344de690891bc93c077530 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:28 +0200 Subject: mtd: maps: lantiq-flash: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c index e2f8782..9385205 100644 --- a/drivers/mtd/maps/lantiq-flash.c +++ b/drivers/mtd/maps/lantiq-flash.c @@ -160,7 +160,7 @@ ltq_mtd_probe(struct platform_device *pdev) return -ENXIO; } - ltq_mtd->mtd->owner = THIS_MODULE; + ltq_mtd->mtd->dev.parent = &pdev->dev; cfi = ltq_mtd->map->fldrv_priv; cfi->addr_unlock1 ^= 1; -- cgit v0.10.2 From ab4a6b4938f74f66fae187fd9e5ba5fe8a7836c6 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:29 +0200 Subject: mtd: maps: latch-addr-flash: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c index cadfbe0..6dc97aa 100644 --- a/drivers/mtd/maps/latch-addr-flash.c +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -195,7 +195,7 @@ static int latch_addr_flash_probe(struct platform_device *dev) err = -ENODEV; goto iounmap; } - info->mtd->owner = THIS_MODULE; + info->mtd->dev.parent = &dev->dev; mtd_device_parse_register(info->mtd, NULL, NULL, latch_addr_data->parts, -- cgit v0.10.2 From 201f230b23cb0252679038d9669af21a99f65614 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:30 +0200 Subject: mtd: maps: physmap: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 4305fd6..cc2adbb 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -167,7 +167,6 @@ static int physmap_flash_probe(struct platform_device *dev) } else { devices_found++; } - info->mtd[i]->owner = THIS_MODULE; info->mtd[i]->dev.parent = &dev->dev; } -- cgit v0.10.2 From 0df415598ef6551070267b1d8c9dc63f4ac00117 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:31 +0200 Subject: mtd: maps: physmap_of: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 3e614e9..e46b4e9 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -290,7 +290,6 @@ static int of_flash_probe(struct platform_device *dev) } else { info->list_size++; } - info->list[i].mtd->owner = THIS_MODULE; info->list[i].mtd->dev.parent = &dev->dev; } -- cgit v0.10.2 From 7f6b5dbfd8d95c200ac0de8e5f5842369eee3839 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:32 +0200 Subject: mtd: maps: plat_ram: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 4b65c08..5157289 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -210,7 +210,6 @@ static int platram_probe(struct platform_device *pdev) goto exit_free; } - info->mtd->owner = THIS_MODULE; info->mtd->dev.parent = &pdev->dev; platram_setrw(info, PLATRAM_RW); -- cgit v0.10.2 From 2451581f94e3f5c0e9d75ab5a59f3d5b0f557ca3 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:33 +0200 Subject: mtd: maps: pxa2xx-flash: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 5ff6cc5..7497090 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -93,7 +93,7 @@ static int pxa2xx_flash_probe(struct platform_device *pdev) iounmap(info->map.cached); return -EIO; } - info->mtd->owner = THIS_MODULE; + info->mtd->dev.parent = &pdev->dev; mtd_device_parse_register(info->mtd, probes, NULL, flash->parts, flash->nr_parts); -- cgit v0.10.2 From 9aa7e50276c17d0658f1035ffe3480085f2a7471 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:34 +0200 Subject: mtd: maps: rbtx4939-flash: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index 5a7551a..18ee3614 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c @@ -96,7 +96,7 @@ static int rbtx4939_flash_probe(struct platform_device *dev) err = -ENXIO; goto err_out; } - info->mtd->owner = THIS_MODULE; + info->mtd.dev.parent = &dev->dev; err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts, pdata->nr_parts); -- cgit v0.10.2 From 72169755cf36ce28bed83d6742d28ce4157f7538 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:35 +0200 Subject: mtd: maps: sa1100-flash: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Incidentally, it seems the owner field in the concatenated mtds is not actually used, so this shouldn't make much of a difference anyway. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 892ad6a..142fc3d 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -117,7 +117,6 @@ static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *r ret = -ENXIO; goto err; } - subdev->mtd->owner = THIS_MODULE; printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n", phys, (unsigned)(subdev->mtd->size >> 20), @@ -234,6 +233,7 @@ static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev, if (info->mtd == NULL) ret = -ENXIO; } + info->mtd->dev.parent = &pdev->dev; if (ret == 0) return info; -- cgit v0.10.2 From 03c287d21f52e0cb368d6577bd75db3cc9e42b0b Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:36 +0200 Subject: mtd: nand: atmel_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 46010bd..8989473 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -2126,7 +2126,7 @@ static int atmel_nand_probe(struct platform_device *pdev) nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; - mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; /* Set address of NAND IO lines */ nand_chip->IO_ADDR_R = host->io_base; -- cgit v0.10.2 From e1cdd89f3f78f00f93311b913e8b64979fcd7498 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:37 +0200 Subject: mtd: nand: au1550nd: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index c0c3be1..08a130f 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -439,7 +439,7 @@ static int au1550nd_probe(struct platform_device *pdev) this = &ctx->chip; ctx->info.priv = this; - ctx->info.owner = THIS_MODULE; + ctx->info.dev.parent = &pdev->dev; /* figure out which CS# r->start belongs to */ cs = find_nand_cs(r->start); -- cgit v0.10.2 From 3f2b80b43cb2dea346d5893797981b016dae7525 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:38 +0200 Subject: mtd: nand: bcm47xxnflash: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/bcm47xxnflash/main.c index 461577c..9ba0c0f 100644 --- a/drivers/mtd/nand/bcm47xxnflash/main.c +++ b/drivers/mtd/nand/bcm47xxnflash/main.c @@ -34,7 +34,7 @@ static int bcm47xxnflash_probe(struct platform_device *pdev) return -ENOMEM; b47n->nand_chip.priv = b47n; - b47n->mtd.owner = THIS_MODULE; + b47n->mtd.dev.parent = &pdev->dev; b47n->mtd.priv = &b47n->nand_chip; /* Required */ b47n->cc = container_of(nflash, struct bcma_drv_cc, nflash); -- cgit v0.10.2 From c7d5955c467c01221fc1ac4b15bba5f255e44454 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:39 +0200 Subject: mtd: nand: bf5xx_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 4d8d4ba..acec344 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -782,7 +782,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev) /* initialise mtd info data struct */ mtd = &info->mtd; mtd->priv = chip; - mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; /* initialise the hardware */ err = bf5xx_nand_hw_init(info); -- cgit v0.10.2 From 2f5997f13f9e98a9a468d803c6d74852bcef1d01 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:40 +0200 Subject: mtd: nand: cafe_nand: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 9a0f45f..aa1dc55 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -604,7 +604,6 @@ static int cafe_nand_probe(struct pci_dev *pdev, mtd->dev.parent = &pdev->dev; mtd->priv = cafe; - mtd->owner = THIS_MODULE; cafe->pdev = pdev; cafe->mmio = pci_iomap(pdev, 0, 0); -- cgit v0.10.2 From 6a44d420b53e7ab742373bb0c3ee36e75c13faee Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:41 +0200 Subject: mtd: nand: davinci_nand: drop owner and name assignment Owner and name are automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index b9080130..c72313d 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -683,9 +683,6 @@ static int nand_davinci_probe(struct platform_device *pdev) info->vaddr = vaddr; info->mtd.priv = &info->chip; - info->mtd.name = dev_name(&pdev->dev); - info->mtd.owner = THIS_MODULE; - info->mtd.dev.parent = &pdev->dev; info->chip.IO_ADDR_R = vaddr; -- cgit v0.10.2 From fbe1a8bf125214807f1062707fc4097721c3c995 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:42 +0200 Subject: mtd: nand: denali: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 43c0771..0db16ef 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1463,7 +1463,6 @@ int denali_init(struct denali_nand_info *denali) /* now that our ISR is registered, we can enable interrupts */ denali_set_intr_modes(denali, true); denali->mtd.name = "denali-nand"; - denali->mtd.owner = THIS_MODULE; denali->mtd.priv = &denali->nand; /* register the driver with the NAND core subsystem */ -- cgit v0.10.2 From 0a1abe7961f1b90bb136157a302490da7d4fe47b Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:43 +0200 Subject: mtd: nand: docg4: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index e5d7bca..510c12d 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -1316,7 +1316,7 @@ static int __init probe_docg4(struct platform_device *pdev) doc = (struct docg4_priv *) (nand + 1); mtd->priv = nand; nand->priv = doc; - mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; doc->virtadr = virtadr; doc->dev = dev; -- cgit v0.10.2 From a309c6be782ceb039f6a86fe8d577d9aab64c529 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:45 +0200 Subject: mtd: nand: fsl_ifc_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 429142e..b9dd41c 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -882,7 +882,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* Fill in fsl_ifc_mtd structure */ priv->mtd.priv = chip; - priv->mtd.owner = THIS_MODULE; + priv->mtd.dev.parent = priv->dev; /* fill in nand_chip structure */ /* set up function call table */ -- cgit v0.10.2 From ddece7b6a6c0402430afe8819472e556e9ae8c75 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:47 +0200 Subject: mtd: nand: fsmc_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 45948e8..9e4fd0f 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -1017,7 +1017,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) mtd->priv = nand; nand->priv = host; - host->mtd.owner = THIS_MODULE; + host->mtd.dev.parent = &pdev->dev; nand->IO_ADDR_R = host->data_va; nand->IO_ADDR_W = host->data_va; nand->cmd_ctrl = fsmc_cmd_ctrl; -- cgit v0.10.2 From 4dc67b1d507133a3d6e93c39993efb6af176d59b Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:49 +0200 Subject: mtd: nand: gpmi-nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 1b8f350..1dbcc81 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1897,7 +1897,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) /* init the MTD data structures */ mtd->priv = chip; mtd->name = "gpmi-nand"; - mtd->owner = THIS_MODULE; + mtd->dev.parent = this->dev; /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ chip->priv = this; -- cgit v0.10.2 From 52364683d5dc4beec9ba61a3fb848821b5cb66c4 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:50 +0200 Subject: mtd: nand: hisi504_nand: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 8dcc7b8..6099aaa 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -737,7 +737,6 @@ static int hisi_nfc_probe(struct platform_device *pdev) } mtd->priv = chip; - mtd->owner = THIS_MODULE; mtd->name = "hisi_nand"; mtd->dev.parent = &pdev->dev; -- cgit v0.10.2 From 7829ab93f1c2334802d259a7e2a4e22a1fd03bd9 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:53 +0200 Subject: mtd: nand: mpc5121_nfc: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 019fe0d..d6bbde4 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -659,6 +659,7 @@ static int mpc5121_nfc_probe(struct platform_device *op) chip = &prv->chip; mtd->priv = chip; + mtd->dev.parent = dev; chip->priv = prv; prv->dev = dev; -- cgit v0.10.2 From ae5d843826d147f81a8d5def8d630caae7ab8011 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:56 +0200 Subject: mtd: nand: nuc900_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index e58c644..f0687f7 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -250,7 +250,7 @@ static int nuc900_nand_probe(struct platform_device *pdev) chip = &(nuc900_nand->chip); nuc900_nand->mtd.priv = chip; - nuc900_nand->mtd.owner = THIS_MODULE; + nuc900_nand->mtd.dev.parent = &pdev->dev; spin_lock_init(&nuc900_nand->lock); nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL); -- cgit v0.10.2 From 84630994fab634baa0250fbd16537f74c8bdbb07 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:58 +0200 Subject: mtd: nand: orion_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 64785f4..ee83749 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -124,7 +124,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) } mtd->priv = nc; - mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; nc->priv = board; nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; -- cgit v0.10.2 From f0aa200ceb4f1bce6826d1c24b5c416a6a8b9faf Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:00 +0200 Subject: mtd: nand: plat_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner and name set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 717cf62..65b9dbbe 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -59,8 +59,7 @@ static int plat_nand_probe(struct platform_device *pdev) data->chip.priv = &data; data->mtd.priv = &data->chip; - data->mtd.owner = THIS_MODULE; - data->mtd.name = dev_name(&pdev->dev); + data->mtd.dev.parent = &pdev->dev; data->chip.IO_ADDR_R = data->io_base; data->chip.IO_ADDR_W = data->io_base; -- cgit v0.10.2 From 2eaac143aa6d433f92819acca1e62a337a73e0a3 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:02 +0200 Subject: mtd: nand: r852: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index cc6bac5..d8bb2be 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -641,7 +641,6 @@ static int r852_register_nand_device(struct r852_device *dev) WARN_ON(dev->card_registred); - dev->mtd->owner = THIS_MODULE; dev->mtd->priv = dev->chip; dev->mtd->dev.parent = &dev->pci_dev->dev; -- cgit v0.10.2 From c4f7dc72e6a56d50e00b40f76be0119ce9abdeec Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:04 +0200 Subject: mtd: nand: sh_flctl: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index c3ce81c..2e4f762 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -1123,6 +1123,7 @@ static int flctl_probe(struct platform_device *pdev) flctl_mtd = &flctl->mtd; nand = &flctl->chip; flctl_mtd->priv = nand; + flctl_mtd->dev.parent = &pdev->dev; flctl->pdev = pdev; flctl->hwecc = pdata->has_hwecc; flctl->holden = pdata->use_holden; -- cgit v0.10.2 From 611425d33763fba57a02c40ee286b0c7152903ea Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:07 +0200 Subject: mtd: nand: sunxi_nand: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 92245a3..d8b809b 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1339,7 +1339,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, mtd = &chip->mtd; mtd->dev.parent = dev; mtd->priv = nand; - mtd->owner = THIS_MODULE; ret = nand_scan_ident(mtd, nsels, NULL); if (ret) -- cgit v0.10.2 From 7b679053cd3348fb8efdd07ce39c4e2e2ad46620 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:08 +0200 Subject: mtd: nand: tmio_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index fb8fd35..befddf0 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -382,6 +382,7 @@ static int tmio_probe(struct platform_device *dev) nand_chip = &tmio->chip; mtd->priv = nand_chip; mtd->name = "tmio-nand"; + mtd->dev.parent = &dev->dev; tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr)); if (!tmio->ccr) -- cgit v0.10.2 From fe003bc8311f3797b802361dbb6465e13dd43a7f Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:10 +0200 Subject: mtd: onenand: generic: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, take advantage of the default owner and name values set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index ab7bda0..125da34 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -60,9 +60,8 @@ static int generic_onenand_probe(struct platform_device *pdev) info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL; info->onenand.irq = platform_get_irq(pdev, 0); - info->mtd.name = dev_name(&pdev->dev); + info->mtd.dev.parent = &pdev->dev; info->mtd.priv = &info->onenand; - info->mtd.owner = THIS_MODULE; if (onenand_scan(&info->mtd, 1)) { err = -ENXIO; -- cgit v0.10.2 From 5c5594c92d2300cc955acd5537e782fbf372caa2 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:54 +0200 Subject: mtd: nand: mxc_nand: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 6862eb3..136e73a 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1517,7 +1517,6 @@ static int mxcnd_probe(struct platform_device *pdev) this = &host->nand; mtd = &host->mtd; mtd->priv = this; - mtd->owner = THIS_MODULE; mtd->dev.parent = &pdev->dev; mtd->name = DRIVER_NAME; -- cgit v0.10.2 From 9e86508f3e538d13c879c7c48a5eecb0509aade6 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:59 +0200 Subject: mtd: nand: pasemi_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 66c345b..83cf021 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -124,7 +124,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev) /* Link the private data with the MTD structure */ pasemi_nand_mtd->priv = chip; - pasemi_nand_mtd->owner = THIS_MODULE; + pasemi_nand_mtd->dev.parent = &ofdev->dev; chip->IO_ADDR_R = of_iomap(np, 0); chip->IO_ADDR_W = chip->IO_ADDR_R; -- cgit v0.10.2 From 0033cf072735a80072c877559180288012f4426d Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:55 +0200 Subject: mtd: nand: ndfc: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 67a1b3f..4f0d62f 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -169,7 +169,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, chip->priv = ndfc; ndfc->mtd.priv = chip; - ndfc->mtd.owner = THIS_MODULE; + ndfc->mtd.dev.parent = &ndfc->ofdev->dev; flash_np = of_get_next_child(node, NULL); if (!flash_np) -- cgit v0.10.2 From 9f80f283ab13c2c055d6001f255cf857be695eb4 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:52 +0200 Subject: mtd: nand: lpc32xx_mlc: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 79c3b78..65b7e03 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -682,7 +682,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; - mtd->owner = THIS_MODULE; mtd->dev.parent = &pdev->dev; /* Get NAND clock */ -- cgit v0.10.2 From 61344d074f4f924cf35aeee445568d9b7750d915 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:12 +0200 Subject: mtd: onenand: samsung: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index 7392595..af0ac1a 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -864,7 +864,6 @@ static int s3c_onenand_probe(struct platform_device *pdev) this = (struct onenand_chip *) &mtd[1]; mtd->priv = this; mtd->dev.parent = &pdev->dev; - mtd->owner = THIS_MODULE; onenand->pdev = pdev; onenand->type = platform_get_device_id(pdev)->driver_data; -- cgit v0.10.2 From 853f1c58c4b2a6f1ec81c335c82d0252b387ab1e Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:57 +0200 Subject: mtd: nand: omap2: show parent device structure in sysfs Make sure the device structure is properly shown in sysfs by properly filling in dev.parent. While at it, make use of the default owner and name values set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 60fa899..7fbf009 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1684,8 +1684,7 @@ static int omap_nand_probe(struct platform_device *pdev) info->ecc_opt = pdata->ecc_opt; mtd = &info->mtd; mtd->priv = &info->nand; - mtd->name = dev_name(&pdev->dev); - mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; nand_chip = &info->nand; nand_chip->ecc.priv = NULL; -- cgit v0.10.2 From 6393b3623340973c9f110060e435c7d42e299120 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:05 +0200 Subject: mtd: nand: sharpsl: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 842c47a..082b600 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -144,7 +144,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev) /* Link the private data with the MTD structure */ sharpsl->mtd.priv = this; - sharpsl->mtd.owner = THIS_MODULE; + sharpsl->mtd.dev.parent = &pdev->dev; platform_set_drvdata(pdev, sharpsl); -- cgit v0.10.2 From 40acbab6a7402ec89fac673bed6bbbc2b430d60e Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:03 +0200 Subject: mtd: nand: s3c2410: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 381f67a..05105ca 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -832,7 +832,6 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, nmtd->info = info; nmtd->mtd.priv = chip; - nmtd->mtd.owner = THIS_MODULE; nmtd->set = set; #ifdef CONFIG_MTD_NAND_S3C2410_HWECC @@ -1016,6 +1015,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info); + nmtd->mtd.dev.parent = &pdev->dev; s3c2410_nand_init_chip(info, nmtd, sets); nmtd->scan_res = nand_scan_ident(&nmtd->mtd, -- cgit v0.10.2 From fd511e218c6a3272ce6a57be9c41e1bc09e37306 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:11 +0200 Subject: mtd: onenand: omap2: drop owner and name assignment Owner and name are automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 646ddd6..3e02856 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -710,9 +710,7 @@ static int omap2_onenand_probe(struct platform_device *pdev) c->onenand.base, c->freq); c->pdev = pdev; - c->mtd.name = dev_name(&pdev->dev); c->mtd.priv = &c->onenand; - c->mtd.owner = THIS_MODULE; c->mtd.dev.parent = &pdev->dev; -- cgit v0.10.2 From efefcaee7610c0762c34123d121ef77f6e550214 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:14 +0200 Subject: staging: mt29f_spinand: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, take advantage of the default owner and name values set by mtdcore. Signed-off-by: Frans Klaver Acked-by: Greg Kroah-Hartman Signed-off-by: Brian Norris diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c index ad30ce4..9a8ddbb 100644 --- a/drivers/staging/mt29f_spinand/mt29f_spinand.c +++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c @@ -912,8 +912,7 @@ static int spinand_probe(struct spi_device *spi_nand) dev_set_drvdata(&spi_nand->dev, mtd); mtd->priv = chip; - mtd->name = dev_name(&spi_nand->dev); - mtd->owner = THIS_MODULE; + mtd->dev.parent = &spi_nand->dev; mtd->oobsize = 64; if (nand_scan(mtd, 1)) -- cgit v0.10.2 From e6c6c284e7344d4b9d262ceb91aea1ba2127fccb Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:48 +0200 Subject: mtd: nand: gpio: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 73c4048..9ab97f9 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -275,7 +275,7 @@ static int gpio_nand_probe(struct platform_device *pdev) chip->cmd_ctrl = gpio_nand_cmd_ctrl; gpiomtd->mtd_info.priv = chip; - gpiomtd->mtd_info.owner = THIS_MODULE; + gpiomtd->mtd_info.dev.parent = &pdev->dev; platform_set_drvdata(pdev, gpiomtd); -- cgit v0.10.2 From 550dab5b357a6103571847d72cd23af069fb872b Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:01 +0200 Subject: mtd: nand: pxa3xx_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 232c707..0024a9c 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1663,7 +1663,7 @@ static int alloc_nand_resource(struct platform_device *pdev) host->cs = cs; host->info_data = info; mtd->priv = host; - mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; -- cgit v0.10.2 From 36645652ad2183130dfc5d4644a69aa2102faa14 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:44 +0200 Subject: mtd: nand: fsl_elbc_nand: show parent device in sysfs Fix a bug where mtd parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index b812074..43cf0f9 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -747,7 +747,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) /* Fill in fsl_elbc_mtd structure */ priv->mtd.priv = chip; - priv->mtd.owner = THIS_MODULE; + priv->mtd.dev.parent = priv->dev; /* set timeout to maximum */ priv->fmr = 15 << FMR_CWTO_SHIFT; -- cgit v0.10.2 From 6031a9c5e39121165617951c4dc337fed72c7bd5 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:06 +0200 Subject: mtd: nand: socrates_nand: drop owner assignment Owner is automatically set by mtdcore. Make use of that. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index d710622..b94f534 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -167,7 +167,6 @@ static int socrates_nand_probe(struct platform_device *ofdev) nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; mtd->name = "socrates_nand"; - mtd->owner = THIS_MODULE; mtd->dev.parent = &ofdev->dev; ppdata.of_node = ofdev->dev.of_node; -- cgit v0.10.2 From 50c65c8ffa4fd08926e671f0c4d9702bfc29c4e2 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:46 +0200 Subject: mtd: nand: fsl_upm: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 72755d7..d326369 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -176,7 +176,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun, fun->chip.dev_ready = fun_chip_ready; fun->mtd.priv = &fun->chip; - fun->mtd.owner = THIS_MODULE; + fun->mtd.dev.parent = fun->dev; flash_np = of_get_next_child(upm_np, NULL); if (!flash_np) -- cgit v0.10.2 From 249eab698068f51c477c9ca55c59c8965025f1dd Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:38:51 +0200 Subject: mtd: nand: jz4740_nand: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index ebf2cce..dc4e844 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -434,7 +434,7 @@ static int jz_nand_probe(struct platform_device *pdev) mtd = &nand->mtd; chip = &nand->chip; mtd->priv = chip; - mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; mtd->name = "jz4740-nand"; chip->ecc.hwctl = jz_nand_hwctl; -- cgit v0.10.2 From 693ad87205ce6ea4c5699e3d2413f644ad903177 Mon Sep 17 00:00:00 2001 From: Frans Klaver Date: Wed, 10 Jun 2015 22:39:09 +0200 Subject: mtd: nand: txx9ndfmc: show parent device in sysfs Fix a bug where parent device symlinks aren't shown in sysfs. While at it, make use of the default owner value set by mtdcore. Signed-off-by: Frans Klaver Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 9c0bc45..8572519 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -323,7 +323,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) continue; chip = &txx9_priv->chip; mtd = &txx9_priv->mtd; - mtd->owner = THIS_MODULE; + mtd->dev.parent = &dev->dev; mtd->priv = chip; -- cgit v0.10.2 From 45aaeff947190e4b57b2d0db4d74ab5eea450825 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 13 Oct 2015 11:22:18 +0200 Subject: mtd: nand: pass page number to ecc->write_xxx() methods The ->read_xxx() methods are all passed the page number the NAND controller is supposed to read, but ->write_xxx() do not have such a parameter. This is a problem if we want to properly implement data scrambling/randomization in order to mitigate MLC sensibility to repeated pattern: to prevent bitflips in adjacent pages in the same block we need to avoid repeating the same pattern at the same offset in those pages, hence the randomizer/scrambler engine need to be passed the page value in order to adapt its seed accordingly. Moreover, adding the page parameter to the ->write_xxx() methods add some consistency to the current API. Signed-off-by: Boris Brezillon CC: Josh Wu CC: Ezequiel Garcia CC: Maxime Ripard CC: Greg Kroah-Hartman CC: Huang Shijie CC: Stefan Agner CC: devel@driverdev.osuosl.org CC: linux-arm-kernel@lists.infradead.org CC: linux-kernel@vger.kernel.org Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 8989473..583cdd9 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -954,7 +954,8 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, } static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required) + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) { struct atmel_nand_host *host = chip->priv; uint32_t *eccpos = chip->ecc.layout->eccpos; @@ -2005,7 +2006,8 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (likely(!raw)) /* Need to write ecc into oob */ - status = chip->ecc.write_page(mtd, chip, buf, oob_required); + status = chip->ecc.write_page(mtd, chip, buf, oob_required, + page); if (status < 0) return status; diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index acec344..61bd216 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -566,7 +566,8 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip } static int bf5xx_nand_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required) + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) { bf5xx_nand_write_buf(mtd, buf, mtd->writesize); bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index ea319a4..7c1c306 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1606,7 +1606,7 @@ out: } static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { struct brcmnand_host *host = chip->priv; void *oob = oob_required ? chip->oob_poi : NULL; @@ -1617,7 +1617,7 @@ static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, static int brcmnand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, - int oob_required) + int oob_required, int page) { struct brcmnand_host *host = chip->priv; void *oob = oob_required ? chip->oob_poi : NULL; diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index aa1dc55..9de78d2 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -516,7 +516,8 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { struct cafe_priv *cafe = mtd->priv; diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 0db16ef..67eb2be 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1114,7 +1114,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip, * by write_page above. */ static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { /* * for regular page writes, we let HW handle all the ECC @@ -1129,7 +1129,8 @@ static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, * write_page() function above. */ static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { /* * for raw page writes, we want to disable ECC and simply write diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 510c12d..408cf69 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -977,13 +977,13 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *nand, } static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { return write_page(mtd, nand, buf, false); } static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { return write_page(mtd, nand, buf, true); } @@ -1113,7 +1113,7 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) /* write first page of block */ write_page_prologue(mtd, g4_addr); - docg4_write_page(mtd, nand, buf, 1); + docg4_write_page(mtd, nand, buf, 1, page); ret = pageprog(mtd); kfree(buf); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 43cf0f9..dcb1f7f 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -715,7 +715,7 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, * waitfunc. */ static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -728,7 +728,7 @@ static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, */ static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, uint32_t data_len, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index b9dd41c..7f4ac8c 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -772,7 +772,7 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, * waitfunc. */ static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { fsl_ifc_write_buf(mtd, buf, mtd->writesize); fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 1dbcc81..2064ada 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1160,7 +1160,7 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, } static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -1446,7 +1446,7 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd, static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, - int oob_required) + int oob_required, int page) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -1533,7 +1533,7 @@ static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, { chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page); - return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1); + return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1, page); } static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) @@ -1717,7 +1717,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Write the first page of the current stride. */ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - chip->ecc.write_page_raw(mtd, chip, buffer, 0); + chip->ecc.write_page_raw(mtd, chip, buffer, 0, page); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); /* Wait for the write to finish. */ diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 6099aaa..0cb2e88 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -590,7 +590,8 @@ static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, } static int hisi_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required) + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) { chip->write_buf(mtd, buf, mtd->writesize); if (oob_required) diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 65b7e03..9686d02 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -495,7 +495,8 @@ static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip, static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { struct lpc32xx_nand_host *host = chip->priv; const uint8_t *oobbuf = chip->oob_poi; diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index cbf4501..40c06b2 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -663,7 +663,8 @@ static int lpc32xx_nand_read_page_raw_syndrome(struct mtd_info *mtd, */ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, + int oob_required, int page) { struct lpc32xx_nand_host *host = chip->priv; uint8_t *pb = chip->oob_poi + chip->ecc.layout->eccpos[0]; @@ -692,7 +693,7 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, - int oob_required) + int oob_required, int page) { /* Raw writes can just use the FIFO interface */ chip->write_buf(mtd, buf, chip->ecc.size * chip->ecc.steps); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index a8230b1..d87c7d0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2155,11 +2155,12 @@ out: * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write * * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { chip->write_buf(mtd, buf, mtd->writesize); if (oob_required) @@ -2174,12 +2175,14 @@ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write * * We need a special oob layout and handling even when ECC isn't checked. */ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2216,9 +2219,11 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write */ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2234,7 +2239,7 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - return chip->ecc.write_page_raw(mtd, chip, buf, 1); + return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); } /** @@ -2243,9 +2248,11 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write */ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2277,11 +2284,12 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @data_len: data length * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write */ static int nand_write_subpage_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, uint32_t data_len, const uint8_t *buf, - int oob_required) + int oob_required, int page) { uint8_t *oob_buf = chip->oob_poi; uint8_t *ecc_calc = chip->buffers->ecccalc; @@ -2336,13 +2344,15 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write * * The hw generator calculates the error syndrome automatically. Therefore we * need a special oob layout and handling. */ static int nand_write_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2406,12 +2416,13 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (unlikely(raw)) status = chip->ecc.write_page_raw(mtd, chip, buf, - oob_required); + oob_required, page); else if (subpage) status = chip->ecc.write_subpage(mtd, chip, offset, data_len, - buf, oob_required); + buf, oob_required, page); else - status = chip->ecc.write_page(mtd, chip, buf, oob_required); + status = chip->ecc.write_page(mtd, chip, buf, oob_required, + page); if (status < 0) return status; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 7fbf009..93f664c 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1500,11 +1500,12 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, * @chip: nand chip info structure * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB + * @page: page * * Custom write page method evolved to support multi sector writing in one shot */ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { int i; uint8_t *ecc_calc = chip->buffers->ecccalc; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 0024a9c..17d7a23 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1160,7 +1160,8 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd, } static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required) + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) { chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 2e4f762..bcba1a9 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -569,7 +569,8 @@ static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, } static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index d8b809b..ef46ac6 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -752,7 +752,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, + int page) { struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; @@ -815,7 +816,7 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, - int oob_required) + int oob_required, int page) { struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index ae1f84e..d275691 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -612,7 +612,7 @@ static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, } static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required) + const uint8_t *buf, int oob_required, int page) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c index 9a8ddbb..405b643 100644 --- a/drivers/staging/mt29f_spinand/mt29f_spinand.c +++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c @@ -612,7 +612,8 @@ static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id) #ifdef CONFIG_MTD_SPINAND_ONDIEECC static int spinand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required) + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) { const uint8_t *p = buf; int eccsize = chip->ecc.size; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 50e1f94..5a9d1d4 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -504,16 +504,16 @@ struct nand_ecc_ctrl { int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page); int (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required); + const uint8_t *buf, int oob_required, int page); int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page); int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offs, uint32_t len, uint8_t *buf, int page); int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, uint32_t data_len, - const uint8_t *data_buf, int oob_required); + const uint8_t *data_buf, int oob_required, int page); int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required); + const uint8_t *buf, int oob_required, int page); int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, int page); int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, -- cgit v0.10.2 From aada20cd2b1af7181523e31231d46ff5a94989b0 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Tue, 13 Oct 2015 08:51:14 +0200 Subject: mtd: spi-nor: s25fl204k supports dual I/0 Signed-off-by: Sean Nyekjaer Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 0397dba..87d66a1 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -654,7 +654,7 @@ static const struct flash_info spi_nor_ids[] = { { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) }, { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) }, - { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K) }, + { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) }, /* SST -- large erase sizes are "overlays", "sectors" are 4K */ { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, -- cgit v0.10.2 From 7c748f5774cb74c6be49fc351007855c96dfb8a3 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Tue, 13 Oct 2015 08:50:30 +0200 Subject: mtd: spi-nor: Add support for s25fl004k Signed-off-by: Sean Nyekjaer Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 87d66a1..0ddf63c 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -649,6 +649,7 @@ static const struct flash_info spi_nor_ids[] = { { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl004k", INFO(0xef4013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, -- cgit v0.10.2 From 801cf21bb5806e39c593bad76b257bba20eaf66f Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:06 -0700 Subject: mtd: spi-nor: make implicit dependency explicit We use BIT() in the header. No real problem for now, but it's better to be accurate. Signed-off-by: Brian Norris diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 672595a..bc4c321 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -10,6 +10,8 @@ #ifndef __LINUX_MTD_SPI_NOR_H #define __LINUX_MTD_SPI_NOR_H +#include + /* * Note on opcode nomenclature: some opcodes have a format like * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number -- cgit v0.10.2 From a8a16454edb364858a0a54e17acaeab329e7b47f Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:07 -0700 Subject: mtd: spi-nor: make bitfield constants more consistent These status bits use different ways of representing similar integer constants -- some are decimal, some are hex. Make them more consistent. At the same time, impose my own preference, since IMO it's clearer what these are when using the BIT() macro. Signed-off-by: Brian Norris diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index bc4c321..768b900 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -63,24 +63,24 @@ #define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ /* Status Register bits. */ -#define SR_WIP 1 /* Write in progress */ -#define SR_WEL 2 /* Write enable latch */ +#define SR_WIP BIT(0) /* Write in progress */ +#define SR_WEL BIT(1) /* Write enable latch */ /* meaning of other SR_* bits may differ between vendors */ -#define SR_BP0 4 /* Block protect 0 */ -#define SR_BP1 8 /* Block protect 1 */ -#define SR_BP2 0x10 /* Block protect 2 */ -#define SR_SRWD 0x80 /* SR write protect */ +#define SR_BP0 BIT(2) /* Block protect 0 */ +#define SR_BP1 BIT(3) /* Block protect 1 */ +#define SR_BP2 BIT(4) /* Block protect 2 */ +#define SR_SRWD BIT(7) /* SR write protect */ -#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ +#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */ /* Enhanced Volatile Configuration Register bits */ -#define EVCR_QUAD_EN_MICRON 0x80 /* Micron Quad I/O */ +#define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */ /* Flag Status Register bits */ -#define FSR_READY 0x80 +#define FSR_READY BIT(7) /* Configuration Register bits. */ -#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ +#define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */ enum read_mode { SPI_NOR_NORMAL = 0, -- cgit v0.10.2 From db4745edb282766f6532d4b0f643c2348e450e25 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:08 -0700 Subject: mtd: spi-nor: add SPI NOR manufacturer IDs These are often similar for CFI (parallel NOR) and for SPI NOR, but they aren't always the same, for various reasons (different namespaces, company acquisitions and renames, etc.). And some don't have CFI_MFR_* entries at all. So let's make a proper place to list the SPI NOR IDs, with all the SPI NOR specific assumptions and comments. Signed-off-by: Brian Norris diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 768b900..88297ee 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -11,6 +11,21 @@ #define __LINUX_MTD_SPI_NOR_H #include +#include + +/* + * Manufacturer IDs + * + * The first byte returned from the flash after sending opcode SPINOR_OP_RDID. + * Sometimes these are the same as CFI IDs, but sometimes they aren't. + */ +#define SNOR_MFR_ATMEL CFI_MFR_ATMEL +#define SNOR_MFR_INTEL CFI_MFR_INTEL +#define SNOR_MFR_MICRON CFI_MFR_ST /* ST Micro <--> Micron */ +#define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX +#define SNOR_MFR_SPANSION CFI_MFR_AMD +#define SNOR_MFR_SST CFI_MFR_SST +#define SNOR_MFR_WINBOND 0xef /* * Note on opcode nomenclature: some opcodes have a format like -- cgit v0.10.2 From f0d2448e9a76db8482e716a067ba5abc713ed981 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:09 -0700 Subject: mtd: spi-nor: use SNOR_MFR_* instead of CFI_MFR_* No functional change, just cosmetic. Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 0ddf63c..d356a1f 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -191,11 +190,11 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, u8 cmd; switch (JEDEC_MFR(info)) { - case CFI_MFR_ST: /* Micron, actually */ + case SNOR_MFR_MICRON: /* Some Micron need WREN command; all will accept it */ need_wren = true; - case CFI_MFR_MACRONIX: - case 0xEF /* winbond */: + case SNOR_MFR_MACRONIX: + case SNOR_MFR_WINBOND: if (need_wren) write_enable(nor); @@ -998,14 +997,14 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) int status; switch (JEDEC_MFR(info)) { - case CFI_MFR_MACRONIX: + case SNOR_MFR_MACRONIX: status = macronix_quad_enable(nor); if (status) { dev_err(nor->dev, "Macronix quad-read not enabled\n"); return -EINVAL; } return status; - case CFI_MFR_ST: + case SNOR_MFR_MICRON: status = micron_quad_enable(nor); if (status) { dev_err(nor->dev, "Micron quad-read not enabled\n"); @@ -1085,9 +1084,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) * up with the software protection bits set */ - if (JEDEC_MFR(info) == CFI_MFR_ATMEL || - JEDEC_MFR(info) == CFI_MFR_INTEL || - JEDEC_MFR(info) == CFI_MFR_SST) { + if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || + JEDEC_MFR(info) == SNOR_MFR_INTEL || + JEDEC_MFR(info) == SNOR_MFR_SST) { write_enable(nor); write_sr(nor, 0); } @@ -1102,8 +1101,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; - /* nor protection support for STmicro chips */ - if (JEDEC_MFR(info) == CFI_MFR_ST) { + /* NOR protection support for STmicro/Micron chips */ + if (JEDEC_MFR(info) == SNOR_MFR_MICRON) { nor->flash_lock = stm_lock; nor->flash_unlock = stm_unlock; } @@ -1197,7 +1196,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) else if (mtd->size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ nor->addr_width = 4; - if (JEDEC_MFR(info) == CFI_MFR_AMD) { + if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) { /* Dedicated 4-byte command set */ switch (nor->flash_read) { case SPI_NOR_QUAD: -- cgit v0.10.2 From f8900258906c3533b91e779e80f75ec80de816c0 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:10 -0700 Subject: mtd: spi-nor: fixup kernel-doc for flash lock/unlock function pointers I got the names of these fields wrong. Signed-off-by: Brian Norris diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 88297ee..75eb1a0 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -145,8 +145,8 @@ struct mtd_info; * @write: [DRIVER-SPECIFIC] write data to the SPI NOR * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR * at the offset @offs - * @lock: [FLASH-SPECIFIC] lock a region of the SPI NOR - * @unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR + * @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR + * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR * @priv: the private data */ struct spi_nor { -- cgit v0.10.2 From 62593cf40b23b523b9fc9334ca61ba6c595ebb09 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:11 -0700 Subject: mtd: spi-nor: refactor block protection functions This code was a bit sloppy, would produce a lot of copy-and-paste, and did not always provide a sensible interface: * It didn't validate the length for LOCK and the offset for UNLOCK, so we were essentially discarding half of the user-supplied data and assuming what they wanted to lock/unlock * It didn't do very good error checking * It didn't make use of the fact that this operation works on power-of-two dimensions So, rewrite this to do proper bit arithmetic rather than a bunch of hard-coded condition tables. Now we have: * More comments on how this was derived * Notes on what is (and isn't) supported * A more exendible function, so we could add support for other protection ranges * More accurate locking - e.g., suppose the top quadrant is locked (75% to 100%); then in the following cases, case (a) will succeed but (b) will not (return -EINVAL): (a) user requests lock 3rd quadrant (50% to 75%) (b) user requests lock 3rd quadrant, minus a few blocks (e.g., 50% to 73%) Case (b) *should* fail, since we'd have to lock blocks that weren't requested. But the old implementation didn't know the difference and would lock the entire second half (50% to 100%) This refactoring work will also help enable the addition of mtd_is_locked() support and potentially the support of bottom boot protection (TB=1). Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index d356a1f..3db0f2b 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -400,72 +400,153 @@ erase_err: return ret; } +static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, + uint64_t *len) +{ + struct mtd_info *mtd = &nor->mtd; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + int shift = ffs(mask) - 1; + int pow; + + if (!(sr & mask)) { + /* No protection */ + *ofs = 0; + *len = 0; + } else { + pow = ((sr & mask) ^ mask) >> shift; + *len = mtd->size >> pow; + *ofs = mtd->size - *len; + } +} + +/* + * Return 1 if the entire region is locked, 0 otherwise + */ +static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, + u8 sr) +{ + loff_t lock_offs; + uint64_t lock_len; + + stm_get_locked_range(nor, sr, &lock_offs, &lock_len); + + return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs); +} + +/* + * Lock a region of the flash. Compatible with ST Micro and similar flash. + * Supports only the block protection bits BP{0,1,2} in the status register + * (SR). Does not support these features found in newer SR bitfields: + * - TB: top/bottom protect - only handle TB=0 (top protect) + * - SEC: sector/block protect - only handle SEC=0 (block protect) + * - CMP: complement protect - only support CMP=0 (range is not complemented) + * + * Sample table portion for 8MB flash (Winbond w25q64fw): + * + * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion + * -------------------------------------------------------------------------- + * X | X | 0 | 0 | 0 | NONE | NONE + * 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64 + * 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32 + * 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16 + * 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8 + * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4 + * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2 + * X | X | 1 | 1 | 1 | 8 MB | ALL + * + * Returns negative on errors, 0 on success. + */ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd = &nor->mtd; - uint32_t offset = ofs; - uint8_t status_old, status_new; - int ret = 0; + u8 status_old, status_new; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; status_old = read_sr(nor); - if (offset < mtd->size - (mtd->size / 2)) - status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; - else if (offset < mtd->size - (mtd->size / 4)) - status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; - else if (offset < mtd->size - (mtd->size / 8)) - status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; - else if (offset < mtd->size - (mtd->size / 16)) - status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; - else if (offset < mtd->size - (mtd->size / 32)) - status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; - else if (offset < mtd->size - (mtd->size / 64)) - status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; - else - status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; + /* SPI NOR always locks to the end */ + if (ofs + len != mtd->size) { + /* Does combined region extend to end? */ + if (!stm_is_locked_sr(nor, ofs + len, mtd->size - ofs - len, + status_old)) + return -EINVAL; + len = mtd->size - ofs; + } + + /* + * Need smallest pow such that: + * + * 1 / (2^pow) <= (len / size) + * + * so (assuming power-of-2 size) we do: + * + * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) + */ + pow = ilog2(mtd->size) - ilog2(len); + val = mask - (pow << shift); + if (val & ~mask) + return -EINVAL; + /* Don't "lock" with no region! */ + if (!(val & mask)) + return -EINVAL; + + status_new = (status_old & ~mask) | val; /* Only modify protection if it will not unlock other areas */ - if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) > - (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { - write_enable(nor); - ret = write_sr(nor, status_new); - } + if ((status_new & mask) <= (status_old & mask)) + return -EINVAL; - return ret; + write_enable(nor); + return write_sr(nor, status_new); } +/* + * Unlock a region of the flash. See stm_lock() for more info + * + * Returns negative on errors, 0 on success. + */ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd = &nor->mtd; - uint32_t offset = ofs; uint8_t status_old, status_new; - int ret = 0; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; status_old = read_sr(nor); - if (offset+len > mtd->size - (mtd->size / 64)) - status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0); - else if (offset+len > mtd->size - (mtd->size / 32)) - status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; - else if (offset+len > mtd->size - (mtd->size / 16)) - status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; - else if (offset+len > mtd->size - (mtd->size / 8)) - status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; - else if (offset+len > mtd->size - (mtd->size / 4)) - status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; - else if (offset+len > mtd->size - (mtd->size / 2)) - status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; - else - status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; + /* Cannot unlock; would unlock larger region than requested */ + if (stm_is_locked_sr(nor, status_old, ofs - mtd->erasesize, + mtd->erasesize)) + return -EINVAL; - /* Only modify protection if it will not lock other areas */ - if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) < - (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { - write_enable(nor); - ret = write_sr(nor, status_new); + /* + * Need largest pow such that: + * + * 1 / (2^pow) >= (len / size) + * + * so (assuming power-of-2 size) we do: + * + * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len)) + */ + pow = ilog2(mtd->size) - order_base_2(mtd->size - (ofs + len)); + if (ofs + len == mtd->size) { + val = 0; /* fully unlocked */ + } else { + val = mask - (pow << shift); + /* Some power-of-two sizes are not supported */ + if (val & ~mask) + return -EINVAL; } - return ret; + status_new = (status_old & ~mask) | val; + + /* Only modify protection if it will not lock other areas */ + if ((status_new & mask) >= (status_old & mask)) + return -EINVAL; + + write_enable(nor); + return write_sr(nor, status_new); } static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -- cgit v0.10.2 From 5bf0e69b67a5600ea7ef178acd57606d1fc2c134 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:12 -0700 Subject: mtd: spi-nor: add mtd_is_locked() support This enables ioctl(MEMISLOCKED). Status can now be reported in the mtdinfo or flash_lock utilities found in mtd-utils. Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 3db0f2b..192aa8c 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -549,6 +549,24 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) return write_sr(nor, status_new); } +/* + * Check if a region of the flash is (completely) locked. See stm_lock() for + * more info. + * + * Returns 1 if entire region is locked, 0 if any portion is unlocked, and + * negative on errors. + */ +static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + int status; + + status = read_sr(nor); + if (status < 0) + return status; + + return stm_is_locked_sr(nor, ofs, len, status); +} + static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct spi_nor *nor = mtd_to_spi_nor(mtd); @@ -579,6 +597,21 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) return ret; } +static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); + if (ret) + return ret; + + ret = nor->flash_is_locked(nor, ofs, len); + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); + return ret; +} + /* Used when the "_ext_id" is two bytes at most */ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ .id = { \ @@ -1186,11 +1219,13 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) if (JEDEC_MFR(info) == SNOR_MFR_MICRON) { nor->flash_lock = stm_lock; nor->flash_unlock = stm_unlock; + nor->flash_is_locked = stm_is_locked; } - if (nor->flash_lock && nor->flash_unlock) { + if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) { mtd->_lock = spi_nor_lock; mtd->_unlock = spi_nor_unlock; + mtd->_is_locked = spi_nor_is_locked; } /* sst nor chips use AAI word program */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 75eb1a0..c8723b6 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -147,6 +147,8 @@ struct mtd_info; * at the offset @offs * @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR + * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is + * completely locked * @priv: the private data */ struct spi_nor { @@ -178,6 +180,7 @@ struct spi_nor { int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); + int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); void *priv; }; -- cgit v0.10.2 From 357ca38d47519c1b90eebfe58e188dd299ee2cef Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:14 -0700 Subject: mtd: spi-nor: support lock/unlock/is_locked for Winbond Many other flash share the same features as ST Micro. I've tested some Winbond flash, so add them. Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 192aa8c..5309920 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1215,8 +1215,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; - /* NOR protection support for STmicro/Micron chips */ - if (JEDEC_MFR(info) == SNOR_MFR_MICRON) { + /* NOR protection support for STmicro/Micron chips and similar */ + if (JEDEC_MFR(info) == SNOR_MFR_MICRON || + JEDEC_MFR(info) == SNOR_MFR_WINBOND) { nor->flash_lock = stm_lock; nor->flash_unlock = stm_unlock; nor->flash_is_locked = stm_is_locked; -- cgit v0.10.2 From c6fc2171b249e73745c497b578b417a2946f1b2f Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:15 -0700 Subject: mtd: spi-nor: disable protection for Winbond flash at startup In case the flash was locked at boot time. Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 5309920..533a0f7 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1194,13 +1194,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) mutex_init(&nor->lock); /* - * Atmel, SST and Intel/Numonyx serial nor tend to power - * up with the software protection bits set + * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up + * with the software protection bits set */ if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || JEDEC_MFR(info) == SNOR_MFR_INTEL || - JEDEC_MFR(info) == SNOR_MFR_SST) { + JEDEC_MFR(info) == SNOR_MFR_SST || + JEDEC_MFR(info) == SNOR_MFR_WINBOND) { write_enable(nor); write_sr(nor, 0); } -- cgit v0.10.2 From a23eb34198c68d9cfdcb7f09f56f2ff416b77c90 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Sep 2015 12:57:13 -0700 Subject: mtd: spi-nor: add DUAL_READ for w25q{32,64}dw These flash support dual and quad read. Tested dual read on the 32 Mbit version. Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 533a0f7..4988390 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -829,10 +829,10 @@ static const struct flash_info spi_nor_ids[] = { { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, -- cgit v0.10.2 From a5c603a22bff27f3aea7e747af4229d75278f3ff Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 12 Oct 2015 13:35:15 -0700 Subject: mtd: fsl-quadspi: fix printk() format warning for size_t Seen when compile-testing on non-32-bit arch: CC drivers/mtd/spi-nor/fsl-quadspi.o drivers/mtd/spi-nor/fsl-quadspi.c: In function 'fsl_qspi_read': drivers/mtd/spi-nor/fsl-quadspi.c:873:2: warning: format '%d' expects argument of type 'int', but argument 6 has type 'size_t' [-Wformat=] dev_dbg(q->dev, "cmd [%x],read from 0x%p, len:%d\n", ^ Also drop the '0x' prefixing to the '%p' formatter, since %p already knows how to format pointers appropriately. Signed-off-by: Brian Norris Acked-by: Han xu diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 2954f89..ca259fa 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -868,7 +868,7 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from, } } - dev_dbg(q->dev, "cmd [%x],read from 0x%p, len:%d\n", + dev_dbg(q->dev, "cmd [%x],read from %p, len:%zd\n", cmd, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, len); -- cgit v0.10.2 From d26a22d06708ba5a181003d44fcd6d0885cec8d4 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 12 Oct 2015 13:35:16 -0700 Subject: mtd: fsl-quadspi: allow building for other ARCHes with COMPILE_TEST This driver doesn't actually need ARCH_MXC to compile. Relax the constraints. Signed-off-by: Brian Norris Acked-by: Han xu diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 89bf4c1..2fe2a7e 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -23,7 +23,8 @@ config MTD_SPI_NOR_USE_4K_SECTORS config SPI_FSL_QUADSPI tristate "Freescale Quad SPI controller" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM help This enables support for the Quad SPI controller in master mode. This controller does not support generic SPI. It only supports -- cgit v0.10.2 From 01a3c625766328200de1656915007537739847aa Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 14 Oct 2015 00:39:44 -0300 Subject: mtd: fsl-quadspi: Include to avoid build error Building for x86 results in the following build errors: drivers/mtd/spi-nor/fsl-quadspi.c: In function 'fsl_qspi_init_lut': >> drivers/mtd/spi-nor/fsl-quadspi.c:355:21: error: 'SZ_16M' undeclared (first use in this function) if (q->nor_size <= SZ_16M) { ^ drivers/mtd/spi-nor/fsl-quadspi.c:355:21: note: each undeclared identifier is reported only once for each function it appears in drivers/mtd/spi-nor/fsl-quadspi.c: In function 'fsl_qspi_read': >> drivers/mtd/spi-nor/fsl-quadspi.c:208:27: error: 'SZ_4M' undeclared (first use in this function) #define QUADSPI_MIN_IOMAP SZ_4M ^ >> drivers/mtd/spi-nor/fsl-quadspi.c:845:25: note: in expansion of macro 'QUADSPI_MIN_IOMAP' q->memmap_len = len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP; Explicitly include to fix the problem. Reported-by: kbuild test robot Signed-off-by: Fabio Estevam Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index ca259fa..dc38389 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -28,6 +28,7 @@ #include #include #include +#include /* Controller needs driver to swap endian */ #define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0) -- cgit v0.10.2 From 2e17497ccfdf0da39595eb98434fdbac93ad3f9b Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 12 Oct 2015 13:33:11 -0700 Subject: mtd: pxa3xx_nand: don't duplicate MTD suspend/resume mtd_{suspend,resume}() get called from mtdcore in a class suspend/resume callback. We don't need to call them again here. In practice, this would actually work OK, as nand_base actually handles nesting OK -- it just might print warnings. Untested, but there are few (no?) users of PM for this driver AFAIK. Signed-off-by: Brian Norris Tested-by: Ezequiel Garcia Acked-by: Ezequiel Garcia diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 17d7a23..4abe755 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1893,32 +1893,19 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) { struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); - struct pxa3xx_nand_platform_data *pdata; - struct mtd_info *mtd; - int cs; - pdata = dev_get_platdata(&pdev->dev); if (info->state) { dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); return -EAGAIN; } - for (cs = 0; cs < pdata->num_cs; cs++) { - mtd = info->host[cs]->mtd; - mtd_suspend(mtd); - } - return 0; } static int pxa3xx_nand_resume(struct platform_device *pdev) { struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); - struct pxa3xx_nand_platform_data *pdata; - struct mtd_info *mtd; - int cs; - pdata = dev_get_platdata(&pdev->dev); /* We don't want to handle interrupt without calling mtd routine */ disable_int(info, NDCR_INT_MASK); @@ -1936,10 +1923,6 @@ static int pxa3xx_nand_resume(struct platform_device *pdev) * all status before resume */ nand_writel(info, NDSR, NDSR_MASK); - for (cs = 0; cs < pdata->num_cs; cs++) { - mtd = info->host[cs]->mtd; - mtd_resume(mtd); - } return 0; } -- cgit v0.10.2 From d3e94f3f2c0f669dfa0d0d51645d5d7d13a9c074 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 12 Oct 2015 14:07:41 -0700 Subject: mtd: pxa3xx_nand: switch to device PM The old PM model is deprecated. This is equivalent. Signed-off-by: Brian Norris Tested-by: Ezequiel Garcia Acked-by: Ezequiel Garcia diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 4abe755..77b3206 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1890,21 +1890,21 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) } #ifdef CONFIG_PM -static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) +static int pxa3xx_nand_suspend(struct device *dev) { - struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = dev_get_drvdata(dev); if (info->state) { - dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); + dev_err(dev, "driver busy, state = %d\n", info->state); return -EAGAIN; } return 0; } -static int pxa3xx_nand_resume(struct platform_device *pdev) +static int pxa3xx_nand_resume(struct device *dev) { - struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = dev_get_drvdata(dev); /* We don't want to handle interrupt without calling mtd routine */ disable_int(info, NDCR_INT_MASK); @@ -1931,15 +1931,19 @@ static int pxa3xx_nand_resume(struct platform_device *pdev) #define pxa3xx_nand_resume NULL #endif +static const struct dev_pm_ops pxa3xx_nand_pm_ops = { + .suspend = pxa3xx_nand_suspend, + .resume = pxa3xx_nand_resume, +}; + static struct platform_driver pxa3xx_nand_driver = { .driver = { .name = "pxa3xx-nand", .of_match_table = pxa3xx_nand_dt_ids, + .pm = &pxa3xx_nand_pm_ops, }, .probe = pxa3xx_nand_probe, .remove = pxa3xx_nand_remove, - .suspend = pxa3xx_nand_suspend, - .resume = pxa3xx_nand_resume, }; module_platform_driver(pxa3xx_nand_driver); -- cgit v0.10.2 From ab84fce518175aa09ec6e1bb50c2b41dad3e610a Mon Sep 17 00:00:00 2001 From: Dongsheng Yang Date: Wed, 30 Sep 2015 09:01:19 +0800 Subject: mtd: mtdram: check offs and len in mtdram->erase We should prevent user to erasing mtd device with an unaligned offset or length. Signed-off-by: Dongsheng Yang Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 8e28508..627a9bc 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -32,8 +32,29 @@ MODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); // We could store these in the mtd structure, but we only support 1 device.. static struct mtd_info *mtd_info; +static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + + /* Start address must align on block boundary */ + if (mtd_mod_by_eb(ofs, mtd)) { + pr_debug("%s: unaligned address\n", __func__); + ret = -EINVAL; + } + + /* Length must align on block boundary */ + if (mtd_mod_by_eb(len, mtd)) { + pr_debug("%s: length not block aligned\n", __func__); + ret = -EINVAL; + } + + return ret; +} + static int ram_erase(struct mtd_info *mtd, struct erase_info *instr) { + if (check_offs_len(mtd, instr->addr, instr->len)) + return -EINVAL; memset((char *)mtd->priv + instr->addr, 0xff, instr->len); instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); -- cgit v0.10.2 From 137d36af4a53858b8db7ca83c8480247118b8bdf Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Thu, 15 Oct 2015 07:56:48 +0200 Subject: mtd: mtd-user: remove stdint.h include Kernel headers should use linux/types.h instead. Signed-off-by: Mikko Rapeli Signed-off-by: Brian Norris diff --git a/include/uapi/mtd/mtd-user.h b/include/uapi/mtd/mtd-user.h index 83327c8..e71d555 100644 --- a/include/uapi/mtd/mtd-user.h +++ b/include/uapi/mtd/mtd-user.h @@ -20,8 +20,6 @@ #ifndef __MTD_USER_H__ #define __MTD_USER_H__ -#include - /* This file is blessed for inclusion by userspace */ #include -- cgit v0.10.2 From ff84d2b76312681c1b751b1cc1ec6c476937f101 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 19 Oct 2015 12:22:57 -0700 Subject: mtd: maps: rbtx4939-flash: fix compile error We got the syntax wrong here. Compile tested this time! Error: drivers/mtd/maps/rbtx4939-flash.c: In function 'rbtx4939_flash_probe': >> drivers/mtd/maps/rbtx4939-flash.c:99:11: error: request for member 'dev' in something not a structure or union info->mtd.dev.parent = &dev->dev; ^ Fixes: 9aa7e50276c1 ("mtd: maps: rbtx4939-flash: show parent device in sysfs") Reported-by: kbuild test robot Signed-off-by: Brian Norris Cc: Frans Klaver diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index 18ee3614..3a06ecf 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c @@ -96,7 +96,7 @@ static int rbtx4939_flash_probe(struct platform_device *dev) err = -ENXIO; goto err_out; } - info->mtd.dev.parent = &dev->dev; + info->mtd->dev.parent = &dev->dev; err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts, pdata->nr_parts); -- cgit v0.10.2 From 44cab9c9300f274d17a76ebe9c9628365bca0b3e Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Sat, 17 Oct 2015 21:09:29 +0300 Subject: mtd: lpc32xx_slc: fix warnings caused by enabling unprepared clock If common clock framework is configured, the driver generates a warning, which is fixed by this change: WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:727 clk_core_enable+0x2c/0xa4() Modules linked in: CPU: 0 PID: 1 Comm: swapper Not tainted 4.3.0-rc2+ #201 Hardware name: LPC32XX SoC (Flattened Device Tree) Backtrace: [<>] (dump_backtrace) from [<>] (show_stack+0x18/0x1c) [<>] (show_stack) from [<>] (dump_stack+0x20/0x28) [<>] (dump_stack) from [<>] (warn_slowpath_common+0x90/0xb8) [<>] (warn_slowpath_common) from [<>] (warn_slowpath_null+0x24/0x2c) [<>] (warn_slowpath_null) from [<>] (clk_core_enable+0x2c/0xa4) [<>] (clk_core_enable) from [<>] (clk_enable+0x24/0x38) [<>] (clk_enable) from [<>] (lpc32xx_nand_probe+0x290/0x568) [<>] (lpc32xx_nand_probe) from [<>] (platform_drv_probe+0x50/0xa0) [<>] (platform_drv_probe) from [<>] (driver_probe_device+0x18c/0x408) [<>] (driver_probe_device) from [<>] (__driver_attach+0x70/0x94) [<>] (__driver_attach) from [<>] (bus_for_each_dev+0x74/0x98) [<>] (bus_for_each_dev) from [<>] (driver_attach+0x20/0x28) [<>] (driver_attach) from [<>] (bus_add_driver+0x11c/0x248) [<>] (bus_add_driver) from [<>] (driver_register+0xa4/0xe8) [<>] (driver_register) from [<>] (__platform_driver_register+0x50/0x64) [<>] (__platform_driver_register) from [<>] (lpc32xx_nand_driver_init+0x18/0x20) [<>] (lpc32xx_nand_driver_init) from [<>] (do_one_initcall+0x11c/0x1dc) [<>] (do_one_initcall) from [<>] (kernel_init_freeable+0x10c/0x1d4) [<>] (kernel_init_freeable) from [<>] (kernel_init+0x10/0xec) [<>] (kernel_init) from [<>] (ret_from_fork+0x14/0x24) Signed-off-by: Vladimir Zapolskiy Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index 40c06b2..4f3d4eb 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -814,7 +814,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = -ENOENT; goto err_exit1; } - clk_enable(host->clk); + clk_prepare_enable(host->clk); /* Set NAND IO addresses and command/ready functions */ chip->IO_ADDR_R = SLC_DATA(host->io_base); @@ -919,7 +919,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) err_exit3: dma_release_channel(host->dma_chan); err_exit2: - clk_disable(host->clk); + clk_disable_unprepare(host->clk); err_exit1: lpc32xx_wp_enable(host); @@ -943,7 +943,7 @@ static int lpc32xx_nand_remove(struct platform_device *pdev) tmp &= ~SLCCFG_CE_LOW; writel(tmp, SLC_CTRL(host->io_base)); - clk_disable(host->clk); + clk_disable_unprepare(host->clk); lpc32xx_wp_enable(host); return 0; @@ -955,7 +955,7 @@ static int lpc32xx_nand_resume(struct platform_device *pdev) struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); /* Re-enable NAND clock */ - clk_enable(host->clk); + clk_prepare_enable(host->clk); /* Fresh init of NAND controller */ lpc32xx_nand_setup(host); @@ -980,7 +980,7 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) lpc32xx_wp_enable(host); /* Disable clock */ - clk_disable(host->clk); + clk_disable_unprepare(host->clk); return 0; } -- cgit v0.10.2 From 64862dbc98ca0f57022802e8e286c596d8c183e9 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Sat, 17 Oct 2015 21:09:30 +0300 Subject: mtd: lpc32xx_mlc: fix warnings caused by enabling unprepared clock If common clock framework is configured, the driver generates a warning, which is fixed by this change: WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:727 clk_core_enable+0x2c/0xa4() Modules linked in: CPU: 0 PID: 1 Comm: swapper Not tainted 4.3.0-rc2+ #206 Hardware name: LPC32XX SoC (Flattened Device Tree) Backtrace: [<>] (dump_backtrace) from [<>] (show_stack+0x18/0x1c) [<>] (show_stack) from [<>] (dump_stack+0x20/0x28) [<>] (dump_stack) from [<>] (warn_slowpath_common+0x90/0xb8) [<>] (warn_slowpath_common) from [<>] (warn_slowpath_null+0x24/0x2c) [<>] (warn_slowpath_null) from [<>] (clk_core_enable+0x2c/0xa4) [<>] (clk_core_enable) from [<>] (clk_enable+0x24/0x38) [<>] (clk_enable) from [<>] (lpc32xx_nand_probe+0x208/0x248) [<>] (lpc32xx_nand_probe) from [<>] (platform_drv_probe+0x50/0xa0) [<>] (platform_drv_probe) from [<>] (driver_probe_device+0x18c/0x408) [<>] (driver_probe_device) from [<>] (__driver_attach+0x70/0x94) [<>] (__driver_attach) from [<>] (bus_for_each_dev+0x74/0x98) [<>] (bus_for_each_dev) from [<>] (driver_attach+0x20/0x28) [<>] (driver_attach) from [<>] (bus_add_driver+0x11c/0x248) [<>] (bus_add_driver) from [<>] (driver_register+0xa4/0xe8) [<>] (driver_register) from [<>] (__platform_driver_register+0x50/0x64) [<>] (__platform_driver_register) from [<>] (lpc32xx_nand_driver_init+0x18/0x20) [<>] (lpc32xx_nand_driver_init) from [<>] (do_one_initcall+0x11c/0x1dc) [<>] (do_one_initcall) from [<>] (kernel_init_freeable+0x10c/0x1d4) [<>] (kernel_init_freeable) from [<>] (kernel_init+0x10/0xec) [<>] (kernel_init) from [<>] (ret_from_fork+0x14/0x24) Signed-off-by: Vladimir Zapolskiy Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 9686d02..3475109 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -692,7 +692,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = -ENOENT; goto err_exit1; } - clk_enable(host->clk); + clk_prepare_enable(host->clk); nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; nand_chip->dev_ready = lpc32xx_nand_device_ready; @@ -800,7 +800,7 @@ err_exit3: if (use_dma) dma_release_channel(host->dma_chan); err_exit2: - clk_disable(host->clk); + clk_disable_unprepare(host->clk); clk_put(host->clk); err_exit1: lpc32xx_wp_enable(host); @@ -822,7 +822,7 @@ static int lpc32xx_nand_remove(struct platform_device *pdev) if (use_dma) dma_release_channel(host->dma_chan); - clk_disable(host->clk); + clk_disable_unprepare(host->clk); clk_put(host->clk); lpc32xx_wp_enable(host); @@ -837,7 +837,7 @@ static int lpc32xx_nand_resume(struct platform_device *pdev) struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); /* Re-enable NAND clock */ - clk_enable(host->clk); + clk_prepare_enable(host->clk); /* Fresh init of NAND controller */ lpc32xx_nand_setup(host); @@ -856,7 +856,7 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) lpc32xx_wp_enable(host); /* Disable clock */ - clk_disable(host->clk); + clk_disable_unprepare(host->clk); return 0; } -- cgit v0.10.2 From ab53a571a4777c36324a030a632281daaf505118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20T=C3=A9nart?= Date: Wed, 21 Oct 2015 10:29:00 +0200 Subject: mtd: pxa3xx_nand: fix some compile issues on non-ARM arches Using readsl() result in a build error on i386. Fix this by using ioread32_rep() instead, to allow compile testing the pxa3xx nand driver on other architectures later. Suggested-by: Arnd Bergmann Signed-off-by: Antoine Tenart Acked-by: Ezequiel Garcia Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 77b3206..91605c1 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -532,7 +532,7 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) * the polling on the last read. */ while (len > 8) { - readsl(info->mmio_base + NDDB, data, 8); + ioread32_rep(info->mmio_base + NDDB, data, 8); ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val, val & NDSR_RDDREQ, 1000, 5000); @@ -547,7 +547,7 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) } } - readsl(info->mmio_base + NDDB, data, len); + ioread32_rep(info->mmio_base + NDDB, data, len); } static void handle_data_pio(struct pxa3xx_nand_info *info) -- cgit v0.10.2 From 3f225b7f52c428e6dccfd28f4cdd93dfe1c936c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20T=C3=A9nart?= Date: Wed, 21 Oct 2015 10:29:02 +0200 Subject: mtd: pxa3xx_nand: add helpers to setup the timings Add helpers to setup the timings in the pxa3xx driver. These helpers allow to either make use of the nand framework nand_sdr_timings or the pxa3xx specific pxa3xx_nand_host, for compatibility reasons. Signed-off-by: Antoine Tenart Acked-by: Ezequiel Garcia Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 91605c1..54f9283 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -418,6 +418,128 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, nand_writel(info, NDTR1CS0, ndtr1); } +static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host, + const struct nand_sdr_timings *t) +{ + struct pxa3xx_nand_info *info = host->info_data; + struct nand_chip *chip = &host->chip; + unsigned long nand_clk = clk_get_rate(info->clk); + uint32_t ndtr0, ndtr1; + + u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000); + u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000); + u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000); + u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000); + u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000); + u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000); + u32 tR = chip->chip_delay * 1000; + u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000); + u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000); + + /* fallback to a default value if tR = 0 */ + if (!tR) + tR = 20000; + + ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) | + NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) | + NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) | + NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) | + NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) | + NDTR0_tRP(ns2cycle(tRP_min, nand_clk)); + + ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) | + NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) | + NDTR1_tAR(ns2cycle(tAR_min, nand_clk)); + + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; + nand_writel(info, NDTR0CS0, ndtr0); + nand_writel(info, NDTR1CS0, ndtr1); +} + +static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host, + unsigned int *flash_width, + unsigned int *dfc_width) +{ + struct nand_chip *chip = &host->chip; + struct pxa3xx_nand_info *info = host->info_data; + const struct pxa3xx_nand_flash *f = NULL; + int i, id, ntypes; + + ntypes = ARRAY_SIZE(builtin_flash_types); + + chip->cmdfunc(host->mtd, NAND_CMD_READID, 0x00, -1); + + id = chip->read_byte(host->mtd); + id |= chip->read_byte(host->mtd) << 0x8; + + for (i = 0; i < ntypes; i++) { + f = &builtin_flash_types[i]; + + if (f->chip_id == id) + break; + } + + if (i == ntypes) { + dev_err(&info->pdev->dev, "Error: timings not found\n"); + return -EINVAL; + } + + pxa3xx_nand_set_timing(host, f->timing); + + *flash_width = f->flash_width; + *dfc_width = f->dfc_width; + + return 0; +} + +static int pxa3xx_nand_init_timings_onfi(struct pxa3xx_nand_host *host, + int mode) +{ + const struct nand_sdr_timings *timings; + + mode = fls(mode) - 1; + if (mode < 0) + mode = 0; + + timings = onfi_async_timing_mode_to_sdr_timings(mode); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + pxa3xx_nand_set_sdr_timing(host, timings); + + return 0; +} + +static int pxa3xx_nand_init(struct pxa3xx_nand_host *host) +{ + struct nand_chip *chip = &host->chip; + struct pxa3xx_nand_info *info = host->info_data; + unsigned int flash_width = 0, dfc_width = 0; + int mode, err; + + mode = onfi_get_async_timing_mode(chip); + if (mode == ONFI_TIMING_MODE_UNKNOWN) { + err = pxa3xx_nand_init_timings_compat(host, &flash_width, + &dfc_width); + if (err) + return err; + + if (flash_width == 16) { + info->reg_ndcr |= NDCR_DWIDTH_M; + chip->options |= NAND_BUSWIDTH_16; + } + + info->reg_ndcr |= (dfc_width == 16) ? NDCR_DWIDTH_C : 0; + } else { + err = pxa3xx_nand_init_timings_onfi(host, mode); + if (err) + return err; + } + + return 0; +} + /* * Set the data and OOB size, depending on the selected * spare and ECC configuration. -- cgit v0.10.2 From f19fe9833d3cb7d95ccd4da6bd83de1d4c77592f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20T=C3=A9nart?= Date: Wed, 21 Oct 2015 10:29:03 +0200 Subject: mtd: pxa3xx_nand: rework flash detection and timing setup Rework the pxa3xx_nand driver to allow using functions exported by the nand framework to detect the flash and the timings. Then setup the timings using the helpers previously added. Signed-off-by: Antoine Tenart Acked-by: Ezequiel Garcia Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 54f9283..2761282 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1396,45 +1396,23 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) return NAND_STATUS_READY; } -static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, - const struct pxa3xx_nand_flash *f) +static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info) { struct platform_device *pdev = info->pdev; 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 */ - - if (f->page_size != 2048 && f->page_size != 512) { - dev_err(&pdev->dev, "Current only support 2048 and 512 size\n"); - return -EINVAL; - } - - if (f->flash_width != 16 && f->flash_width != 8) { - dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n"); - return -EINVAL; - } - - /* calculate addressing information */ - host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; - - if (f->num_blocks * f->page_per_block > 65536) - host->row_addr_cycles = 3; - else - host->row_addr_cycles = 2; - - ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; - ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0; - ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0; - ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0; - ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; - - ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); - ndcr |= NDCR_SPARE_EN; /* enable spare by default */ + struct mtd_info *mtd = host->mtd; + struct nand_chip *chip = mtd->priv; - info->reg_ndcr = ndcr; + /* configure default flash values */ + info->reg_ndcr = 0x0; /* enable all interrupts */ + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); + info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */ + info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; + info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0; + info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; - pxa3xx_nand_set_timing(host, f->timing); return 0; } @@ -1514,19 +1492,23 @@ static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) kfree(info->data_buff); } -static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info) +static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host) { + struct pxa3xx_nand_info *info = host->info_data; struct mtd_info *mtd; struct nand_chip *chip; + const struct nand_sdr_timings *timings; int ret; mtd = info->host[info->cs]->mtd; chip = mtd->priv; /* use the common timing to make a try */ - ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]); - if (ret) - return ret; + timings = onfi_async_timing_mode_to_sdr_timings(0); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + pxa3xx_nand_set_sdr_timing(host, timings); chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0); ret = chip->waitfunc(mtd, chip); @@ -1611,12 +1593,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) struct pxa3xx_nand_info *info = host->info_data; struct platform_device *pdev = info->pdev; 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; - uint32_t id = -1; - uint64_t chipsize; - int i, ret, num; + int ret; uint16_t ecc_strength, ecc_step; if (pdata->keep_config && !pxa3xx_nand_detect_config(info)) @@ -1625,7 +1603,11 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) /* Set a default chunk size */ info->chunk_size = 512; - ret = pxa3xx_nand_sensing(info); + ret = pxa3xx_nand_config_flash(info); + if (ret) + return ret; + + ret = pxa3xx_nand_sensing(host); if (ret) { dev_info(&info->pdev->dev, "There is no chip on cs %d!\n", info->cs); @@ -1633,50 +1615,6 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) return ret; } - chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0); - id = *((uint16_t *)(info->data_buff)); - if (id != 0) - dev_info(&info->pdev->dev, "Detect a flash id %x\n", id); - else { - dev_warn(&info->pdev->dev, - "Read out ID 0, potential timing set wrong!!\n"); - - return -EINVAL; - } - - num = ARRAY_SIZE(builtin_flash_types) - 1; - for (i = 0; i < num; i++) { - f = &builtin_flash_types[i + 1]; - - /* find the chip in default list */ - if (f->chip_id == id) - break; - } - - if (i >= (ARRAY_SIZE(builtin_flash_types) - 1)) { - dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n"); - - return -EINVAL; - } - - ret = pxa3xx_nand_config_flash(info, f); - if (ret) { - dev_err(&info->pdev->dev, "ERROR! Configure failed\n"); - return ret; - } - - memset(pxa3xx_flash_ids, 0, sizeof(pxa3xx_flash_ids)); - - pxa3xx_flash_ids[0].name = f->name; - pxa3xx_flash_ids[0].dev_id = (f->chip_id >> 8) & 0xffff; - pxa3xx_flash_ids[0].pagesize = f->page_size; - chipsize = (uint64_t)f->num_blocks * f->page_per_block * f->page_size; - pxa3xx_flash_ids[0].chipsize = chipsize >> 20; - pxa3xx_flash_ids[0].erasesize = f->page_size * f->page_per_block; - if (f->flash_width == 16) - pxa3xx_flash_ids[0].options = NAND_BUSWIDTH_16; - pxa3xx_flash_ids[1].name = NULL; - def = pxa3xx_flash_ids; KEEP_CONFIG: info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; if (info->reg_ndcr & NDCR_DWIDTH_M) @@ -1686,9 +1624,18 @@ KEEP_CONFIG: if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) nand_writel(info, NDECCCTRL, 0x0); - if (nand_scan_ident(mtd, 1, def)) + if (nand_scan_ident(mtd, 1, NULL)) return -ENODEV; + if (!pdata->keep_config) { + ret = pxa3xx_nand_init(host); + if (ret) { + dev_err(&info->pdev->dev, "Failed to init nand: %d\n", + ret); + return ret; + } + } + if (pdata->flash_bbt) { /* * We'll use a bad block table stored in-flash and don't -- cgit v0.10.2 From 89c1702da717164fcff633594e130f54dd563499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20T=C3=A9nart?= Date: Wed, 21 Oct 2015 10:29:04 +0200 Subject: mtd: pxa3xx_nand: clean up the pxa3xx timings With the previous modifications, lots of pxa3xx specific definitions can be removed. Signed-off-by: Antoine Tenart Acked-by: Ezequiel Garcia Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 2761282..e453ae9 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -266,14 +266,9 @@ struct pxa3xx_nand_timing { }; struct pxa3xx_nand_flash { - char *name; uint32_t chip_id; - unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */ - unsigned int page_size; /* Page size in bytes (PAGE_SZ) */ unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ - unsigned int num_blocks; /* Number of physical blocks in Flash */ - struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ }; @@ -285,15 +280,14 @@ static struct pxa3xx_nand_timing timing[] = { }; static struct pxa3xx_nand_flash builtin_flash_types[] = { -{ "DEFAULT FLASH", 0, 0, 2048, 8, 8, 0, &timing[0] }, -{ "64MiB 16-bit", 0x46ec, 32, 512, 16, 16, 4096, &timing[1] }, -{ "256MiB 8-bit", 0xdaec, 64, 2048, 8, 8, 2048, &timing[1] }, -{ "4GiB 8-bit", 0xd7ec, 128, 4096, 8, 8, 8192, &timing[1] }, -{ "128MiB 8-bit", 0xa12c, 64, 2048, 8, 8, 1024, &timing[2] }, -{ "128MiB 16-bit", 0xb12c, 64, 2048, 16, 16, 1024, &timing[2] }, -{ "512MiB 8-bit", 0xdc2c, 64, 2048, 8, 8, 4096, &timing[2] }, -{ "512MiB 16-bit", 0xcc2c, 64, 2048, 16, 16, 4096, &timing[2] }, -{ "256MiB 16-bit", 0xba20, 64, 2048, 16, 16, 2048, &timing[3] }, + { 0x46ec, 16, 16, &timing[1] }, + { 0xdaec, 8, 8, &timing[1] }, + { 0xd7ec, 8, 8, &timing[1] }, + { 0xa12c, 8, 8, &timing[2] }, + { 0xb12c, 16, 16, &timing[2] }, + { 0xdc2c, 8, 8, &timing[2] }, + { 0xcc2c, 16, 16, &timing[2] }, + { 0xba20, 16, 16, &timing[3] }, }; static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; @@ -354,9 +348,6 @@ static struct nand_ecclayout ecc_layout_4KB_bch8bit = { .oobfree = { } }; -/* Define a default flash type setting serve as flash detecting only */ -#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0]) - #define NDTR0_tCH(c) (min((c), 7) << 19) #define NDTR0_tCS(c) (min((c), 7) << 16) #define NDTR0_tWH(c) (min((c), 7) << 11) -- cgit v0.10.2 From 2382960793c2480277ae98a891ea5aa566e06ff1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 19 Oct 2015 13:20:05 +0300 Subject: mtd: docg3: off by one in doc_register_sysfs() Smatch found a bug in the error handling: drivers/mtd/devices/docg3.c:1634 doc_register_sysfs() error: buffer overflow 'doc_sys_attrs' 4 <= 4 The problem is that if the very last device_create_file() fails, then we are beyond the end of the array. Actually, any time i == 3 then there is a problem. We can fix this an simplify the code at the same time by moving the !ret conditions out of the for loops and using a goto instead. Signed-off-by: Dan Carpenter Acked-by: Robert Jarzmik Signed-off-by: Brian Norris diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index f00d0da..c3a2695 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1620,20 +1620,30 @@ static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = { static int doc_register_sysfs(struct platform_device *pdev, struct docg3_cascade *cascade) { - int ret = 0, floor, i = 0; struct device *dev = &pdev->dev; + int floor; + int ret; + int i; - for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS && - cascade->floors[floor]; floor++) - for (i = 0; !ret && i < 4; i++) + for (floor = 0; + floor < DOC_MAX_NBFLOORS && cascade->floors[floor]; + floor++) { + for (i = 0; i < 4; i++) { ret = device_create_file(dev, &doc_sys_attrs[floor][i]); - if (!ret) - return 0; + if (ret) + goto remove_files; + } + } + + return 0; + +remove_files: do { while (--i >= 0) device_remove_file(dev, &doc_sys_attrs[floor][i]); i = 4; } while (--floor >= 0); + return ret; } -- cgit v0.10.2 From b70af9bef49bd9a5f4e7a2327d9074e29653e665 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Tue, 6 Oct 2015 14:52:07 +0100 Subject: mtd: nand: increase ready wait timeout and report timeouts If nand_wait_ready() times out, this is silently ignored, and its caller will then proceed to read from/write to the chip before it is ready. This can potentially result in corruption with no indication as to why. While a 20ms timeout seems like it should be plenty enough, certain behaviour can cause it to timeout much earlier than expected. The situation which prompted this change was that CPU 0, which is responsible for updating jiffies, was holding interrupts disabled for a fairly long time while writing to the console during a printk, causing several jiffies updates to be delayed. If CPU 1 happens to enter the timeout loop in nand_wait_ready() just before CPU 0 re- enables interrupts and updates jiffies, CPU 1 will immediately time out when the delayed jiffies updates are made. The result of this is that nand_wait_ready() actually waits less time than the NAND chip would normally take to be ready, and then read_page() proceeds to read out bad data from the chip. The situation described above may seem unlikely, but in fact it can be reproduced almost every boot on the MIPS Creator Ci20. Therefore, this patch increases the timeout to 400ms. This should be enough to cover cases where jiffies updates get delayed. In nand_wait() the timeout was previously chosen based on whether erasing or programming. This is changed to be 400ms unconditionally as well to avoid similar problems there. nand_wait() is also slightly refactored to be consistent with nand_wait{,_status}_ready(). These changes should have no effect during normal operation. Debugging this was made more difficult by the misleading comment above nand_wait_ready() stating "The timeout is caught later" - no timeout was ever reported, leading me away from the real source of the problem. Therefore, a pr_warn() is added when a timeout does occur so that it is easier to pinpoint similar problems in future. Signed-off-by: Alex Smith Signed-off-by: Harvey Hunt Reviewed-by: Niklas Cassel Cc: Alex Smith Cc: Zubair Lutfullah Kakakhel Cc: David Woodhouse Cc: Niklas Cassel Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index d87c7d0..cc74142 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -543,23 +543,32 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) } } -/* Wait for the ready pin, after a command. The timeout is caught later. */ +/** + * nand_wait_ready - [GENERIC] Wait for the ready pin after commands. + * @mtd: MTD device structure + * + * Wait for the ready pin after a command, and warn if a timeout occurs. + */ void nand_wait_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - unsigned long timeo = jiffies + msecs_to_jiffies(20); + unsigned long timeo = 400; - /* 400ms timeout */ if (in_interrupt() || oops_in_progress) - return panic_nand_wait_ready(mtd, 400); + return panic_nand_wait_ready(mtd, timeo); led_trigger_event(nand_led_trigger, LED_FULL); /* Wait until command is processed or timeout occurs */ + timeo = jiffies + msecs_to_jiffies(timeo); do { if (chip->dev_ready(mtd)) - break; - touch_softlockup_watchdog(); + goto out; + cond_resched(); } while (time_before(jiffies, timeo)); + + pr_warn_ratelimited( + "timeout while waiting for chip to become ready\n"); +out: led_trigger_event(nand_led_trigger, LED_OFF); } EXPORT_SYMBOL_GPL(nand_wait_ready); @@ -885,15 +894,13 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: MTD device structure * @chip: NAND chip structure * - * Wait for command done. This applies to erase and program only. Erase can - * take up to 400ms and program up to 20ms according to general NAND and - * SmartMedia specs. + * Wait for command done. This applies to erase and program only. */ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) { - int status, state = chip->state; - unsigned long timeo = (state == FL_ERASING ? 400 : 20); + int status; + unsigned long timeo = 400; led_trigger_event(nand_led_trigger, LED_FULL); @@ -909,7 +916,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) panic_nand_wait(mtd, chip, timeo); else { timeo = jiffies + msecs_to_jiffies(timeo); - while (time_before(jiffies, timeo)) { + do { if (chip->dev_ready) { if (chip->dev_ready(mtd)) break; @@ -918,7 +925,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) break; } cond_resched(); - } + } while (time_before(jiffies, timeo)); } led_trigger_event(nand_led_trigger, LED_OFF); -- cgit v0.10.2 From 48c25cf441182e629e52e7f9fa56c2019e75fb00 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 29 Sep 2015 14:11:56 -0700 Subject: mtd: nand: vf610_nfc: use nand_check_erased_ecc_chunk() helper Signed-off-by: Brian Norris Reviewed-by: Boris Brezillon diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index d275691..8805d63 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -561,7 +561,6 @@ static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS; u8 ecc_status; u8 ecc_count; - int flips; int flips_threshold = nfc->chip.ecc.strength / 2; ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff; @@ -578,16 +577,9 @@ static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, * On an erased page, bit count (including OOB) should be zero or * at least less then half of the ECC strength. */ - flips = count_written_bits(dat, nfc->chip.ecc.size, flips_threshold); - flips += count_written_bits(oob, mtd->oobsize, flips_threshold); - - if (unlikely(flips > flips_threshold)) - return -EINVAL; - - /* Erased page. */ - memset(dat, 0xff, nfc->chip.ecc.size); - memset(oob, 0xff, mtd->oobsize); - return flips; + return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob, + mtd->oobsize, NULL, 0, + flips_threshold); } static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, -- cgit v0.10.2 From e278fc71b2c63905d3631b8d7b12ab7bcba9d2be Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 19 Oct 2015 08:40:13 +0200 Subject: mtd: fsmc_nand: Add BCH4 SW ECC support for SPEAr600 This patch adds support for 4-bit ECC BCH4 for the SPEAr600 SoC. This can be used by boards equipped with a NAND chip that requires 4-bit ECC strength. The SPEAr600 HW ECC only supports 1-bit ECC strength. To enable SW BCH4, you need to specify this in your nand controller DT node: nand-ecc-mode = "soft_bch"; nand-ecc-strength = <4>; nand-ecc-step-size = <512>; Tested on a custom SPEAr600 board. Signed-off-by: Stefan Roese Cc: Linus Walleij Cc: Viresh Kumar [Brian: tweaked the comments a bit] Signed-off-by: Brian Norris diff --git a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt index 5235cbc..32636eb 100644 --- a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt +++ b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt @@ -30,6 +30,12 @@ Optional properties: 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). +- nand-ecc-mode : see nand.txt +- nand-ecc-strength : see nand.txt +- nand-ecc-step-size : see nand.txt + +Can support 1-bit HW ECC (default) or if stronger correction is required, +software-based BCH. Example: diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 9e4fd0f..07af3dc 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -1023,12 +1023,17 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->cmd_ctrl = fsmc_cmd_ctrl; nand->chip_delay = 30; + /* + * Setup default ECC mode. nand_dt_init() called from nand_scan_ident() + * can overwrite this value if the DT provides a different value. + */ nand->ecc.mode = NAND_ECC_HW; nand->ecc.hwctl = fsmc_enable_hwecc; nand->ecc.size = 512; nand->options = pdata->options; nand->select_chip = fsmc_select_chip; nand->badblockbits = 7; + nand->flash_node = np; if (pdata->width == FSMC_NAND_BW16) nand->options |= NAND_BUSWIDTH_16; @@ -1070,11 +1075,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.correct = fsmc_bch8_correct_data; nand->ecc.bytes = 13; nand->ecc.strength = 8; - } else { - nand->ecc.calculate = fsmc_read_hwecc_ecc1; - nand->ecc.correct = nand_correct_data; - nand->ecc.bytes = 3; - nand->ecc.strength = 1; } /* @@ -1115,22 +1115,47 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) goto err_probe; } } else { - switch (host->mtd.oobsize) { - case 16: - nand->ecc.layout = &fsmc_ecc1_16_layout; + switch (nand->ecc.mode) { + case NAND_ECC_HW: + dev_info(&pdev->dev, "Using 1-bit HW ECC scheme\n"); + nand->ecc.calculate = fsmc_read_hwecc_ecc1; + nand->ecc.correct = nand_correct_data; + nand->ecc.bytes = 3; + nand->ecc.strength = 1; break; - case 64: - nand->ecc.layout = &fsmc_ecc1_64_layout; - break; - case 128: - nand->ecc.layout = &fsmc_ecc1_128_layout; + + case NAND_ECC_SOFT_BCH: + dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n"); break; + default: - dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", - mtd->oobsize); - ret = -EINVAL; + dev_err(&pdev->dev, "Unsupported ECC mode!\n"); goto err_probe; } + + /* + * Don't set layout for BCH4 SW ECC. This will be + * generated later in nand_bch_init() later. + */ + if (nand->ecc.mode != NAND_ECC_SOFT_BCH) { + switch (host->mtd.oobsize) { + case 16: + nand->ecc.layout = &fsmc_ecc1_16_layout; + break; + case 64: + nand->ecc.layout = &fsmc_ecc1_64_layout; + break; + case 128: + nand->ecc.layout = &fsmc_ecc1_128_layout; + break; + default: + dev_warn(&pdev->dev, + "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + ret = -EINVAL; + goto err_probe; + } + } } /* Second stage of scan to fill MTD data-structures */ -- cgit v0.10.2 From af30c0a00aa0d086a820c2ec75544c07611834d7 Mon Sep 17 00:00:00 2001 From: Shraddha Barke Date: Thu, 22 Oct 2015 20:29:54 +0530 Subject: mtd: tests: Replace timeval with ktime_t Changes the 32-bit time type timeval to the 64-bit time type ktime_t, since 32-bit systems using struct timeval will break in the year 2038. Correspondingly change do_gettimeofday() to ktime_get() since ktime_get returns a ktime_t, but do_gettimeofday returns a struct timeval.Here, ktime_get() is used instead of ktime_get_real() since ktime_get() uses monotonic clock. Signed-off-by: Shraddha Barke Reviewed-by: Arnd Bergmann Signed-off-by: Brian Norris diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c index 5a6f31a..0b89418 100644 --- a/drivers/mtd/tests/speedtest.c +++ b/drivers/mtd/tests/speedtest.c @@ -22,6 +22,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -49,7 +50,7 @@ static int pgsize; static int ebcnt; static int pgcnt; static int goodebcnt; -static struct timeval start, finish; +static ktime_t start, finish; static int multiblock_erase(int ebnum, int blocks) { @@ -168,12 +169,12 @@ static int read_eraseblock_by_2pages(int ebnum) static inline void start_timing(void) { - do_gettimeofday(&start); + start = ktime_get(); } static inline void stop_timing(void) { - do_gettimeofday(&finish); + finish = ktime_get(); } static long calc_speed(void) @@ -181,8 +182,7 @@ 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; + ms = ktime_ms_delta(finish, start); if (ms == 0) return 0; k = (uint64_t)goodebcnt * (mtd->erasesize / 1024) * 1000; diff --git a/drivers/mtd/tests/torturetest.c b/drivers/mtd/tests/torturetest.c index e5d6e6d..93c2729 100644 --- a/drivers/mtd/tests/torturetest.c +++ b/drivers/mtd/tests/torturetest.c @@ -26,6 +26,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -79,18 +80,18 @@ static unsigned char *check_buf; static unsigned int erase_cycles; static int pgsize; -static struct timeval start, finish; +static ktime_t start, finish; static void report_corrupt(unsigned char *read, unsigned char *written); static inline void start_timing(void) { - do_gettimeofday(&start); + start = ktime_get(); } static inline void stop_timing(void) { - do_gettimeofday(&finish); + finish = ktime_get(); } /* @@ -333,8 +334,7 @@ static int __init tort_init(void) long ms; stop_timing(); - ms = (finish.tv_sec - start.tv_sec) * 1000 + - (finish.tv_usec - start.tv_usec) / 1000; + ms = ktime_ms_delta(finish, start); pr_info("%08u erase cycles done, took %lu " "milliseconds (%lu seconds)\n", erase_cycles, ms, ms / 1000); -- cgit v0.10.2 From 3e00ed0e984bbec47f5e531bad3cf36885aa5d83 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 1 Jun 2015 16:17:19 -0700 Subject: mtd: fixup corner case error handling in mtd_device_parse_register() Since commit 3efe41be224c ("mtd: implement common reboot notifier boilerplate"), we might try to register a reboot notifier for an MTD that failed to register. Let's avoid this by making the error path clearer. Signed-off-by: Brian Norris Reviewed-by: Richard Weinberger diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index bbcba0d..a2e76ac 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -588,9 +588,15 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, else ret = nr_parts; } + /* Didn't come up with either parsed OR fallback partitions */ + if (ret < 0) { + pr_info("mtd: failed to find partitions\n"); + goto out; + } - if (ret >= 0) - ret = mtd_add_device_partitions(mtd, real_parts, ret); + ret = mtd_add_device_partitions(mtd, real_parts, ret); + if (ret) + goto out; /* * FIXME: some drivers unfortunately call this function more than once. @@ -605,6 +611,7 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, register_reboot_notifier(&mtd->reboot_notifier); } +out: kfree(real_parts); return ret; } -- cgit v0.10.2 From be0dbff8b46d69bd738f63b4fe0cf64417f776b0 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 1 Jun 2015 16:17:20 -0700 Subject: mtd: warn when registering the same master many times When CONFIG_MTD_PARTITIONED_MASTER=y, it is fatal to call mtd_device_parse_register() twice on the same MTD, as we try to register the same device/kobject multipile times. When CONFIG_MTD_PARTITIONED_MASTER=n, calling mtd_device_parse_register() is more of just a nuisance, as we can mostly navigate around any conflicting actions. But anyway, doing so is a Bad Thing (TM), and we should complain loudly for any drivers that try to do this. Signed-off-by: Brian Norris Reviewed-by: Richard Weinberger diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index a2e76ac..41dc501 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -387,6 +387,14 @@ int add_mtd_device(struct mtd_info *mtd) struct mtd_notifier *not; int i, error; + /* + * May occur, for instance, on buggy drivers which call + * mtd_device_parse_register() multiple times on the same master MTD, + * especially with CONFIG_MTD_PARTITIONED_MASTER=y. + */ + if (WARN_ONCE(mtd->backing_dev_info, "MTD already registered\n")) + return -EEXIST; + mtd->backing_dev_info = &mtd_bdi; BUG_ON(mtd->writesize == 0); @@ -606,6 +614,7 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, * does cause problems with parse_mtd_partitions() above (e.g., * cmdlineparts will register partitions more than once). */ + WARN_ONCE(mtd->reboot_notifier.notifier_call, "MTD already registered\n"); if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) { mtd->reboot_notifier.notifier_call = mtd_reboot_notifier; register_reboot_notifier(&mtd->reboot_notifier); -- cgit v0.10.2 From 04850c4d8613127a9b488321c0ad83bff7519311 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Fri, 23 Oct 2015 13:18:28 -0500 Subject: mtd: fsl-quadspi: fix macro collision problems with READ/WRITE Change the READ/WRITE to FSL_READ/FSL_WRITE to resolve any possible namespace collisions with READ/WRITE macros (e.g., from ). Problems have been seen, for example, on mips: >> drivers/mtd/spi-nor/fsl-quadspi.c:186:5: error: 'LUT_0' undeclared (first use in this function) ((LUT_##ins) << INSTR0_SHIFT)) ^ >> drivers/mtd/spi-nor/fsl-quadspi.c:188:30: note: in expansion of macro 'LUT0' On SPARC: drivers/mtd/spi-nor/fsl-quadspi.c: In function 'fsl_qspi_init_lut': drivers/mtd/spi-nor/fsl-quadspi.c:369:1: error: 'LUT_0' undeclared (first use in this function) drivers/mtd/spi-nor/fsl-quadspi.c:418:1: error: pasting "LUT_" and "(" does not give a valid preprocessing token drivers/mtd/spi-nor/fsl-quadspi.c:418:2: error: implicit declaration of function 'LUT_' And surely on others. Fixes: d26a22d06708 ("mtd: fsl-quadspi: allow building for other ARCHes with COMPILE_TEST") Reported-by: Guenter Roeck Reported-by: kbuild test robot Signed-off-by: Han Xu [Brian: rewrote commit description] Signed-off-by: Brian Norris diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index dc38389..7b10ed4 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -155,15 +155,15 @@ #define LUT_MODE 4 #define LUT_MODE2 5 #define LUT_MODE4 6 -#define LUT_READ 7 -#define LUT_WRITE 8 +#define LUT_FSL_READ 7 +#define LUT_FSL_WRITE 8 #define LUT_JMP_ON_CS 9 #define LUT_ADDR_DDR 10 #define LUT_MODE_DDR 11 #define LUT_MODE2_DDR 12 #define LUT_MODE4_DDR 13 -#define LUT_READ_DDR 14 -#define LUT_WRITE_DDR 15 +#define LUT_FSL_READ_DDR 14 +#define LUT_FSL_WRITE_DDR 15 #define LUT_DATA_LEARN 16 /* @@ -366,7 +366,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), base + QUADSPI_LUT(lut_base)); - writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo), + writel(LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo), base + QUADSPI_LUT(lut_base + 1)); /* Write enable */ @@ -387,11 +387,11 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), base + QUADSPI_LUT(lut_base)); - writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); + writel(LUT0(FSL_WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); /* Read Status */ lut_base = SEQID_RDSR * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(FSL_READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Erase a sector */ @@ -410,17 +410,17 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* READ ID */ lut_base = SEQID_RDID * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(FSL_READ, PAD1, 0x8), base + QUADSPI_LUT(lut_base)); /* Write Register */ lut_base = SEQID_WRSR * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2), + writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(FSL_WRITE, PAD1, 0x2), base + QUADSPI_LUT(lut_base)); /* Read Configuration Register */ lut_base = SEQID_RDCR * 4; - writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(FSL_READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Write disable */ -- cgit v0.10.2 From 5a2415b0769233194f20d3906c3ffc6a2033317c Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Sun, 11 Oct 2015 13:03:47 -0700 Subject: mtd: mtdpart: Do not fail mtd probe when parsing partitions fails Due to wrong assumption in ofpart ofpart fails on Exynos on SPI chips with no partitions because the subnode containing controller data confuses the ofpart parser. Thus compiling in ofpart support automatically fails probing any SPI NOR flash without partitions on Exynos. Compiling in a partitioning scheme should not cause probe of otherwise valid device to fail. Instead, let's do the following: * try parsers until one succeeds * if no parser succeeds, report the first error we saw * even in the failure case, allow MTD to probe, with fallback partitions or no partitions at all -- the master device will still be registered Issue report and comments initially by Michal Suchanek. Reported-by: Michal Suchanek Signed-off-by: Brian Norris diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 41dc501..b1eea48 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -598,8 +598,10 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, } /* Didn't come up with either parsed OR fallback partitions */ if (ret < 0) { - pr_info("mtd: failed to find partitions\n"); - goto out; + pr_info("mtd: failed to find partitions; one or more parsers reports errors (%d)\n", + ret); + /* Don't abort on errors; we can still use unpartitioned MTD */ + ret = 0; } ret = mtd_add_device_partitions(mtd, real_parts, ret); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index f5279ea..f8ba153 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -755,12 +755,12 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, struct mtd_part_parser_data *data) { struct mtd_part_parser *parser; - int ret = 0; + int ret, err = 0; if (!types) types = default_mtd_part_types; - for ( ; ret <= 0 && *types; types++) { + for ( ; *types; types++) { pr_debug("%s: parsing partitions %s\n", master->name, *types); parser = get_partition_parser(*types); if (!parser && !request_module("%s", *types)) @@ -776,10 +776,16 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, if (ret > 0) { printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", ret, parser->name, master->name); - break; + return ret; } + /* + * Stash the first error we see; only report it if no parser + * succeeds + */ + if (ret < 0 && !err) + err = ret; } - return ret; + return err; } int mtd_is_partition(const struct mtd_info *mtd) -- cgit v0.10.2 From 3f06d2a912b55c53e9efbd799f7205dbfe041029 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 30 Oct 2015 12:29:19 +0530 Subject: mtd: brcmnand: factor out CFG and CFG_EXT bitfields Use enum instead of magic numbers for CFG and CFG_EXT bitfields. Signed-off-by: Brian Norris Tested-by: Anup Patel diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index 7c1c306..3bd31a60 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -344,6 +344,28 @@ static const u8 brcmnand_cs_offsets_cs0[] = { [BRCMNAND_CS_TIMING2] = 0x14, }; +/* + * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had + * one config register, but once the bitfields overflowed, newer controllers + * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around. + */ +enum { + CFG_BLK_ADR_BYTES_SHIFT = 8, + CFG_COL_ADR_BYTES_SHIFT = 12, + CFG_FUL_ADR_BYTES_SHIFT = 16, + CFG_BUS_WIDTH_SHIFT = 23, + CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT), + CFG_DEVICE_SIZE_SHIFT = 24, + + /* Only for pre-v7.1 (with no CFG_EXT register) */ + CFG_PAGE_SIZE_SHIFT = 20, + CFG_BLK_SIZE_SHIFT = 28, + + /* Only for v7.1+ (with CFG_EXT register) */ + CFG_EXT_PAGE_SIZE_SHIFT = 0, + CFG_EXT_BLK_SIZE_SHIFT = 4, +}; + /* BRCMNAND_INTFC_STATUS */ enum { INTFC_FLASH_STATUS = GENMASK(7, 0), @@ -1720,17 +1742,19 @@ static int brcmnand_set_cfg(struct brcmnand_host *host, } device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE); - tmp = (cfg->blk_adr_bytes << 8) | - (cfg->col_adr_bytes << 12) | - (cfg->ful_adr_bytes << 16) | - (!!(cfg->device_width == 16) << 23) | - (device_size << 24); + tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) | + (cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) | + (cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) | + (!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) | + (device_size << CFG_DEVICE_SIZE_SHIFT); if (cfg_offs == cfg_ext_offs) { - tmp |= (page_size << 20) | (block_size << 28); + tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) | + (block_size << CFG_BLK_SIZE_SHIFT); nand_writereg(ctrl, cfg_offs, tmp); } else { nand_writereg(ctrl, cfg_offs, tmp); - tmp = page_size | (block_size << 4); + tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) | + (block_size << CFG_EXT_BLK_SIZE_SHIFT); nand_writereg(ctrl, cfg_ext_offs, tmp); } -- cgit v0.10.2 From 4d1ea982a9beb4e47bba41131973329e2a012b38 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 30 Oct 2015 12:29:20 +0530 Subject: mtd: brcmnand: Force 8bit mode before doing nand_scan_ident() Just like other NAND controllers, the NAND READID command only works in 8bit mode for all versions of BRCMNAND controller. This patch forces 8bit mode for each NAND CS in brcmnand_init_cs() before doing nand_scan_ident() to ensure that BRCMNAND controller is in 8bit mode when NAND READID command is issued. Signed-off-by: Anup Patel Reviewed-by: Ray Jui Reviewed-by: Scott Branden Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index 3bd31a60..12c6190 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1913,6 +1913,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host) struct mtd_info *mtd; struct nand_chip *chip; int ret; + u16 cfg_offs; struct mtd_part_parser_data ppdata = { .of_node = dn }; ret = of_property_read_u32(dn, "reg", &host->cs); @@ -1955,6 +1956,15 @@ static int brcmnand_init_cs(struct brcmnand_host *host) chip->controller = &ctrl->controller; + /* + * The bootloader might have configured 16bit mode but + * NAND READID command only works in 8bit mode. We force + * 8bit mode here to ensure that NAND READID commands works. + */ + cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + nand_writereg(ctrl, cfg_offs, + nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH); + if (nand_scan_ident(mtd, 1, NULL)) return -ENXIO; -- cgit v0.10.2 From fe2585e9c29a650af26824684e5033757fd6bc0c Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Wed, 28 Oct 2015 15:48:06 -0700 Subject: doc: dt: mtd: support partitions in a special 'partitions' subnode To avoid conflict with other drivers using subnodes of the mtd device create only one ofpart-specific node rather than any number of arbitrary partition subnodes. Signed-off-by: Michal Suchanek Acked-by: Rob Herring Signed-off-by: Brian Norris diff --git a/Documentation/devicetree/bindings/mtd/partition.txt b/Documentation/devicetree/bindings/mtd/partition.txt index 8e5557d..f1e2a02 100644 --- a/Documentation/devicetree/bindings/mtd/partition.txt +++ b/Documentation/devicetree/bindings/mtd/partition.txt @@ -4,10 +4,17 @@ 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: +The partition table should be a subnode of the mtd node and should be named +'partitions'. Partitions are defined in subnodes of the partitions node. + +For backwards compatibility partitions as direct subnodes of the mtd device are +supported. This use is discouraged. +NOTE: also for backwards compatibility, direct subnodes that have a compatible +string are not considered partitions, as they may be used for other bindings. + +#address-cells & #size-cells must both be present in the partitions subnode of the +mtd device. There are two valid values for both: <1>: for partitions that require a single 32-bit cell to represent their size/address (aka the value is below 4 GiB) <2>: for partitions that require two 32-bit cells to represent their @@ -28,44 +35,50 @@ Examples: flash@0 { - #address-cells = <1>; - #size-cells = <1>; + partitions { + #address-cells = <1>; + #size-cells = <1>; - partition@0 { - label = "u-boot"; - reg = <0x0000000 0x100000>; - read-only; - }; + partition@0 { + label = "u-boot"; + reg = <0x0000000 0x100000>; + read-only; + }; - uimage@100000 { - reg = <0x0100000 0x200000>; + uimage@100000 { + reg = <0x0100000 0x200000>; + }; }; }; flash@1 { - #address-cells = <1>; - #size-cells = <2>; + partitions { + #address-cells = <1>; + #size-cells = <2>; - /* a 4 GiB partition */ - partition@0 { - label = "filesystem"; - reg = <0x00000000 0x1 0x00000000>; + /* a 4 GiB partition */ + partition@0 { + label = "filesystem"; + reg = <0x00000000 0x1 0x00000000>; + }; }; }; flash@2 { - #address-cells = <2>; - #size-cells = <2>; + partitions { + #address-cells = <2>; + #size-cells = <2>; - /* an 8 GiB partition */ - partition@0 { - label = "filesystem #1"; - reg = <0x0 0x00000000 0x2 0x00000000>; - }; + /* an 8 GiB partition */ + partition@0 { + label = "filesystem #1"; + reg = <0x0 0x00000000 0x2 0x00000000>; + }; - /* a 4 GiB partition */ - partition@200000000 { - label = "filesystem #2"; - reg = <0x2 0x00000000 0x1 0x00000000>; + /* a 4 GiB partition */ + partition@200000000 { + label = "filesystem #2"; + reg = <0x2 0x00000000 0x1 0x00000000>; + }; }; }; -- cgit v0.10.2 From 5cfdedb7b9a0fe38aa4838bfe66fb9ebc2c9ce15 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Tue, 18 Aug 2015 15:34:09 +0000 Subject: mtd: ofpart: move ofpart partitions to a dedicated dt node Parsing direct subnodes of a mtd device as partitions is unreliable since the mtd device is also part of its bus subsystem and can contain bus data in subnodes. Move ofpart data to a subnode of its own so it is clear which data is part of the partition layout. Signed-off-by: Michal Suchanek Signed-off-by: Brian Norris diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index aa26c32..669c345 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -29,23 +29,33 @@ static int parse_ofpart_partitions(struct mtd_info *master, struct mtd_partition **pparts, struct mtd_part_parser_data *data) { - struct device_node *node; + struct device_node *mtd_node; + struct device_node *ofpart_node; const char *partname; struct device_node *pp; - int nr_parts, i; + int nr_parts, i, ret = 0; + bool dedicated = true; if (!data) return 0; - node = data->of_node; - if (!node) + mtd_node = data->of_node; + if (!mtd_node) return 0; + ofpart_node = of_get_child_by_name(mtd_node, "partitions"); + if (!ofpart_node) { + pr_warn("%s: 'partitions' subnode not found on %s. Trying to parse direct subnodes as partitions.\n", + master->name, mtd_node->full_name); + ofpart_node = mtd_node; + dedicated = false; + } + /* First count the subnodes */ nr_parts = 0; - for_each_child_of_node(node, pp) { - if (node_has_compatible(pp)) + for_each_child_of_node(ofpart_node, pp) { + if (!dedicated && node_has_compatible(pp)) continue; nr_parts++; @@ -59,22 +69,36 @@ static int parse_ofpart_partitions(struct mtd_info *master, return -ENOMEM; i = 0; - for_each_child_of_node(node, pp) { + for_each_child_of_node(ofpart_node, pp) { const __be32 *reg; int len; int a_cells, s_cells; - if (node_has_compatible(pp)) + if (!dedicated && node_has_compatible(pp)) continue; reg = of_get_property(pp, "reg", &len); if (!reg) { - nr_parts--; - continue; + if (dedicated) { + pr_debug("%s: ofpart partition %s (%s) missing reg property.\n", + master->name, pp->full_name, + mtd_node->full_name); + goto ofpart_fail; + } else { + nr_parts--; + continue; + } } a_cells = of_n_addr_cells(pp); s_cells = of_n_size_cells(pp); + if (len / 4 != a_cells + s_cells) { + pr_debug("%s: ofpart partition %s (%s) error parsing reg property.\n", + master->name, pp->full_name, + mtd_node->full_name); + goto ofpart_fail; + } + (*pparts)[i].offset = of_read_number(reg, a_cells); (*pparts)[i].size = of_read_number(reg + a_cells, s_cells); @@ -92,15 +116,20 @@ static int parse_ofpart_partitions(struct mtd_info *master, i++; } - if (!i) { - of_node_put(pp); - pr_err("No valid partition found on %s\n", node->full_name); - kfree(*pparts); - *pparts = NULL; - return -EINVAL; - } + if (!nr_parts) + goto ofpart_none; return nr_parts; + +ofpart_fail: + pr_err("%s: error parsing ofpart partition %s (%s)\n", + master->name, pp->full_name, mtd_node->full_name); + ret = -EINVAL; +ofpart_none: + of_node_put(pp); + kfree(*pparts); + *pparts = NULL; + return ret; } static struct mtd_part_parser ofpart_parser = { -- cgit v0.10.2 From f3c63795e90f0c6238306883b6c72f14d5355721 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 26 Oct 2015 10:20:23 -0700 Subject: mtd: blkdevs: fix potential deadlock + lockdep warnings Commit 073db4a51ee4 ("mtd: fix: avoid race condition when accessing mtd->usecount") fixed a race condition but due to poor ordering of the mutex acquisition, introduced a potential deadlock. The deadlock can occur, for example, when rmmod'ing the m25p80 module, which will delete one or more MTDs, along with any corresponding mtdblock devices. This could potentially race with an acquisition of the block device as follows. -> blktrans_open() -> mutex_lock(&dev->lock); -> mutex_lock(&mtd_table_mutex); -> del_mtd_device() -> mutex_lock(&mtd_table_mutex); -> blktrans_notify_remove() -> del_mtd_blktrans_dev() -> mutex_lock(&dev->lock); This is a classic (potential) ABBA deadlock, which can be fixed by making the A->B ordering consistent everywhere. There was no real purpose to the ordering in the original patch, AFAIR, so this shouldn't be a problem. This ordering was actually already present in del_mtd_blktrans_dev(), for one, where the function tried to ensure that its caller already held mtd_table_mutex before it acquired &dev->lock: if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); BUG(); } So, reverse the ordering of acquisition of &dev->lock and &mtd_table_mutex so we always acquire mtd_table_mutex first. Snippets of the lockdep output follow: # modprobe -r m25p80 [ 53.419251] [ 53.420838] ====================================================== [ 53.427300] [ INFO: possible circular locking dependency detected ] [ 53.433865] 4.3.0-rc6 #96 Not tainted [ 53.437686] ------------------------------------------------------- [ 53.444220] modprobe/372 is trying to acquire lock: [ 53.449320] (&new->lock){+.+...}, at: [] del_mtd_blktrans_dev+0x80/0xdc [ 53.457271] [ 53.457271] but task is already holding lock: [ 53.463372] (mtd_table_mutex){+.+.+.}, at: [] del_mtd_device+0x18/0x100 [ 53.471321] [ 53.471321] which lock already depends on the new lock. [ 53.471321] [ 53.479856] [ 53.479856] the existing dependency chain (in reverse order) is: [ 53.487660] -> #1 (mtd_table_mutex){+.+.+.}: [ 53.492331] [] blktrans_open+0x34/0x1a4 [ 53.497879] [] __blkdev_get+0xc4/0x3b0 [ 53.503364] [] blkdev_get+0x108/0x320 [ 53.508743] [] do_dentry_open+0x218/0x314 [ 53.514496] [] path_openat+0x4c0/0xf9c [ 53.519959] [] do_filp_open+0x5c/0xc0 [ 53.525336] [] do_sys_open+0xfc/0x1cc [ 53.530716] [] ret_fast_syscall+0x0/0x1c [ 53.536375] -> #0 (&new->lock){+.+...}: [ 53.540587] [] mutex_lock_nested+0x38/0x3cc [ 53.546504] [] del_mtd_blktrans_dev+0x80/0xdc [ 53.552606] [] blktrans_notify_remove+0x7c/0x84 [ 53.558891] [] del_mtd_device+0x74/0x100 [ 53.564544] [] del_mtd_partitions+0x80/0xc8 [ 53.570451] [] mtd_device_unregister+0x24/0x48 [ 53.576637] [] spi_drv_remove+0x1c/0x34 [ 53.582207] [] __device_release_driver+0x88/0x114 [ 53.588663] [] device_release_driver+0x20/0x2c [ 53.594843] [] bus_remove_device+0xd8/0x108 [ 53.600748] [] device_del+0x10c/0x210 [ 53.606127] [] device_unregister+0xc/0x20 [ 53.611849] [] __unregister+0x10/0x20 [ 53.617211] [] device_for_each_child+0x50/0x7c [ 53.623387] [] spi_unregister_master+0x58/0x8c [ 53.629578] [] release_nodes+0x15c/0x1c8 [ 53.635223] [] __device_release_driver+0x90/0x114 [ 53.641689] [] driver_detach+0xb4/0xb8 [ 53.647147] [] bus_remove_driver+0x4c/0xa0 [ 53.652970] [] SyS_delete_module+0x11c/0x1e4 [ 53.658976] [] ret_fast_syscall+0x0/0x1c [ 53.664621] [ 53.664621] other info that might help us debug this: [ 53.664621] [ 53.672979] Possible unsafe locking scenario: [ 53.672979] [ 53.679169] CPU0 CPU1 [ 53.683900] ---- ---- [ 53.688633] lock(mtd_table_mutex); [ 53.692383] lock(&new->lock); [ 53.698306] lock(mtd_table_mutex); [ 53.704658] lock(&new->lock); [ 53.707946] [ 53.707946] *** DEADLOCK *** Fixes: 073db4a51ee4 ("mtd: fix: avoid race condition when accessing mtd->usecount") Reported-by: Felipe Balbi Tested-by: Felipe Balbi Signed-off-by: Brian Norris Cc: diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index cb47d79..f470118 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -192,8 +192,8 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) if (!dev) return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ - mutex_lock(&dev->lock); mutex_lock(&mtd_table_mutex); + mutex_lock(&dev->lock); if (dev->open) goto unlock; @@ -217,8 +217,8 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) unlock: dev->open++; - mutex_unlock(&mtd_table_mutex); mutex_unlock(&dev->lock); + mutex_unlock(&mtd_table_mutex); blktrans_dev_put(dev); return ret; @@ -228,8 +228,8 @@ error_release: error_put: module_put(dev->tr->owner); kref_put(&dev->ref, blktrans_dev_release); - mutex_unlock(&mtd_table_mutex); mutex_unlock(&dev->lock); + mutex_unlock(&mtd_table_mutex); blktrans_dev_put(dev); return ret; } @@ -241,8 +241,8 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode) if (!dev) return; - mutex_lock(&dev->lock); mutex_lock(&mtd_table_mutex); + mutex_lock(&dev->lock); if (--dev->open) goto unlock; @@ -256,8 +256,8 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode) __put_mtd_device(dev->mtd); } unlock: - mutex_unlock(&mtd_table_mutex); mutex_unlock(&dev->lock); + mutex_unlock(&mtd_table_mutex); blktrans_dev_put(dev); } -- cgit v0.10.2 From 74eb9ff54e21dbeefa04ff30e6aaf780b91dd261 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 20 Oct 2015 22:16:00 +0200 Subject: mtd: nand: sunxi: fix sunxi_nfc_hw_ecc_read/write_chunk() The sunxi_nfc_hw_ecc_read/write_chunk() functions try to avoid changing the column address if unnecessary, but the logic to determine whether it's necessary or not is currently wrong: it adds the ecc->bytes value to the current offset where it should actually add ecc->size. Fixes: 913821bdd211 ("mtd: nand: sunxi: introduce sunxi_nfc_hw_ecc_read/write_chunk()") Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index ef46ac6..96f7370 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -588,7 +588,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, sunxi_nfc_read_buf(mtd, data, ecc->size); - if (data_off + ecc->bytes != oob_off) + if (data_off + ecc->size != oob_off) nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); @@ -679,7 +679,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, writel(sunxi_nfc_buf_to_user_data(oob), nfc->regs + NFC_REG_USER_DATA(0)); - if (data_off + ecc->bytes != oob_off) + if (data_off + ecc->size != oob_off) nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); -- cgit v0.10.2 From 28ec8a864f4b67bb801aede7919391531612c7dd Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 20 Oct 2015 22:16:01 +0200 Subject: mtd: nand: sunxi: avoid retrieving data before ECC pass The in-band data are copied twice: before ECC correction and after the ECC engine has fixed all the fixable bitflips. Drop the useless memcpy_fromio operation by passing a NULL pointer when calling sunxi_nfc_read_buf(). Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 96f7370..8247118 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -586,7 +586,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, if (*cur_off != data_off) nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); - sunxi_nfc_read_buf(mtd, data, ecc->size); + sunxi_nfc_read_buf(mtd, NULL, ecc->size); if (data_off + ecc->size != oob_off) nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); -- cgit v0.10.2 From f8479dd6a03cfdc3b14e742045bfd8748cd86bd7 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 3 Nov 2015 17:01:53 -0800 Subject: mtd: don't WARN about overloaded users of mtd->reboot_notifier.notifier_call There are multiple types of users of mtd->reboot_notifier.notifier_call: (1) A while back, the cfi_cmdset_000{1,2} chip drivers implemented a reboot notifier to (on a best effort basis) attempt to reset their flash chips before rebooting. (2) More recently, we implemented a common _reboot() hook so that MTD drivers (particularly, NAND flash) could better halt I/O operations without having to reimplement the same notifier boilerplate. Currently, the WARN_ONCE() condition here was written to handle (2), but at the same time it mis-diagnosed case (1) as an already-registered MTD. Let's fix this by having the WARN_ONCE() condition better imitate the condition that immediately follows it. (Wow, I don't know how I missed that one.) (Side note: Unfortunately, we can't yet combine the reboot notifier code for (1) and (2) with a patch like [1], because some users of (1) also use mtdconcat, and so the mtd_info struct from cfi_cmdset_000{1,2} won't actually get registered with mtdcore, and therefore their reboot notifier won't get registered.) [1] http://patchwork.ozlabs.org/patch/417981/ Suggested-by: Guenter Roeck Signed-off-by: Brian Norris Cc: Jesper Nilsson Cc: linux-cris-kernel@axis.com Tested-by: Ezequiel Garcia Tested-by: Guenter Roeck Signed-off-by: Brian Norris diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index b1eea48..a91cee9 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -616,7 +616,8 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, * does cause problems with parse_mtd_partitions() above (e.g., * cmdlineparts will register partitions more than once). */ - WARN_ONCE(mtd->reboot_notifier.notifier_call, "MTD already registered\n"); + WARN_ONCE(mtd->_reboot && mtd->reboot_notifier.notifier_call, + "MTD already registered\n"); if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) { mtd->reboot_notifier.notifier_call = mtd_reboot_notifier; register_reboot_notifier(&mtd->reboot_notifier); -- cgit v0.10.2