From 56c6855c81c8a6828b5d65aa974cd50f4b67760c Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Thu, 5 Apr 2018 14:12:49 -0500 Subject: mtd: spi-nor: Add Micron MT25QU02 support Add support for a new Micron 2Gb Flash memory part. Datasheet is available: mt25q_qlkt_l_02g_cbb_0.pdf Testing was done on a Stratix10 SoCFPGA Development Kit. Reported-by: Sujith Chidurala Tested-by: Paul Kim Signed-off-by: Thor Thayer Acked-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 5bfa36e95f35..8b459766a4ca 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1087,6 +1087,7 @@ static const struct flash_info spi_nor_ids[] = { { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, + { "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, -- cgit v1.2.3 From c7aa1b77f96f3549ac71ac3eb631c8e47536bcf6 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 7 Apr 2018 15:01:05 +0200 Subject: mtd: spi-nor: add support for ISSI is25lp256 Add support for ISSI is25lp256 spi nor flash. Signed-off-by: Marek Vasut Cc: Angelo Dureghello Cc: Boris Brezillon Cc: Cyrille Pitchen Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 8b459766a4ca..cded32974ed1 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1049,6 +1049,8 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "is25lp128", INFO(0x9d6018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ) }, + { "is25lp256", INFO(0x9d6019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ) }, /* Macronix */ { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) }, -- cgit v1.2.3 From ffa639e069fb55a150a2f14af6762b2c429e6d50 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Tue, 10 Apr 2018 13:49:10 +0530 Subject: mtd: spi-nor: cadence-quadspi: Add DMA support for direct mode reads Add support to use DMA over memory mapped reads in direct mode. This helps in reducing CPU usage from ~100% to ~10% when reading data from flash. For non-DMA'able/vmalloc'd buffers, driver just falls back to CPU based memcpy. Signed-off-by: Vignesh R Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/cadence-quadspi.c | 96 ++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c index 4b8e9183489a..2f3a4d4232b3 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -73,6 +75,10 @@ struct cqspi_st { struct completion transfer_complete; struct mutex bus_mutex; + struct dma_chan *rx_chan; + struct completion rx_dma_complete; + dma_addr_t mmap_phys_base; + int current_cs; int current_page_size; int current_erase_size; @@ -915,11 +921,75 @@ static ssize_t cqspi_write(struct spi_nor *nor, loff_t to, return len; } +static void cqspi_rx_dma_callback(void *param) +{ + struct cqspi_st *cqspi = param; + + complete(&cqspi->rx_dma_complete); +} + +static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf, + loff_t from, size_t len) +{ + struct cqspi_flash_pdata *f_pdata = nor->priv; + struct cqspi_st *cqspi = f_pdata->cqspi; + enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + dma_addr_t dma_src = (dma_addr_t)cqspi->mmap_phys_base + from; + int ret = 0; + struct dma_async_tx_descriptor *tx; + dma_cookie_t cookie; + dma_addr_t dma_dst; + + if (!cqspi->rx_chan || !virt_addr_valid(buf)) { + memcpy_fromio(buf, cqspi->ahb_base + from, len); + return 0; + } + + dma_dst = dma_map_single(nor->dev, buf, len, DMA_DEV_TO_MEM); + if (dma_mapping_error(nor->dev, dma_dst)) { + dev_err(nor->dev, "dma mapping failed\n"); + return -ENOMEM; + } + tx = dmaengine_prep_dma_memcpy(cqspi->rx_chan, dma_dst, dma_src, + len, flags); + if (!tx) { + dev_err(nor->dev, "device_prep_dma_memcpy error\n"); + ret = -EIO; + goto err_unmap; + } + + tx->callback = cqspi_rx_dma_callback; + tx->callback_param = cqspi; + cookie = tx->tx_submit(tx); + reinit_completion(&cqspi->rx_dma_complete); + + ret = dma_submit_error(cookie); + if (ret) { + dev_err(nor->dev, "dma_submit_error %d\n", cookie); + ret = -EIO; + goto err_unmap; + } + + dma_async_issue_pending(cqspi->rx_chan); + ret = wait_for_completion_timeout(&cqspi->rx_dma_complete, + msecs_to_jiffies(len)); + if (ret <= 0) { + dmaengine_terminate_sync(cqspi->rx_chan); + dev_err(nor->dev, "DMA wait_for_completion_timeout\n"); + ret = -ETIMEDOUT; + goto err_unmap; + } + +err_unmap: + dma_unmap_single(nor->dev, dma_dst, len, DMA_DEV_TO_MEM); + + return 0; +} + static ssize_t cqspi_read(struct spi_nor *nor, loff_t from, size_t len, u_char *buf) { struct cqspi_flash_pdata *f_pdata = nor->priv; - struct cqspi_st *cqspi = f_pdata->cqspi; int ret; ret = cqspi_set_protocol(nor, 1); @@ -931,7 +1001,7 @@ static ssize_t cqspi_read(struct spi_nor *nor, loff_t from, return ret; if (f_pdata->use_direct_mode) - memcpy_fromio(buf, cqspi->ahb_base + from, len); + ret = cqspi_direct_read_execute(nor, buf, from, len); else ret = cqspi_indirect_read_execute(nor, buf, from, len); if (ret) @@ -1100,6 +1170,21 @@ static void cqspi_controller_init(struct cqspi_st *cqspi) cqspi_controller_enable(cqspi, 1); } +static void cqspi_request_mmap_dma(struct cqspi_st *cqspi) +{ + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + cqspi->rx_chan = dma_request_chan_by_mask(&mask); + if (IS_ERR(cqspi->rx_chan)) { + dev_err(&cqspi->pdev->dev, "No Rx DMA available\n"); + cqspi->rx_chan = NULL; + } + init_completion(&cqspi->rx_dma_complete); +} + static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np) { const struct spi_nor_hwcaps hwcaps = { @@ -1177,6 +1262,9 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np) f_pdata->use_direct_mode = true; dev_dbg(nor->dev, "using direct mode for %s\n", mtd->name); + + if (!cqspi->rx_chan) + cqspi_request_mmap_dma(cqspi); } } @@ -1237,6 +1325,7 @@ static int cqspi_probe(struct platform_device *pdev) dev_err(dev, "Cannot remap AHB address.\n"); return PTR_ERR(cqspi->ahb_base); } + cqspi->mmap_phys_base = (dma_addr_t)res_ahb->start; cqspi->ahb_size = resource_size(res_ahb); init_completion(&cqspi->transfer_complete); @@ -1307,6 +1396,9 @@ static int cqspi_remove(struct platform_device *pdev) cqspi_controller_enable(cqspi, 0); + if (cqspi->rx_chan) + dma_release_channel(cqspi->rx_chan); + clk_disable_unprepare(cqspi->clk); pm_runtime_put_sync(&pdev->dev); -- cgit v1.2.3 From 640702490d133cc91dec823ac7ef0f1e44eee447 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 10 Apr 2018 17:01:06 -0300 Subject: mtd: spi-nor: hisi: Avoid generic function names Using generic names such as get_if_type() is frowned upon: it suggests a core function (which is not), and then it makes code navigation harder. Given drivers are often used as starting point to write other drivers, generic names tend to spread like the flu. Cure the problem. Signed-off-by: Ezequiel Garcia Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/hisi-sfc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c index 04f9fb5cd9b6..dea7b0c4b339 100644 --- a/drivers/mtd/spi-nor/hisi-sfc.c +++ b/drivers/mtd/spi-nor/hisi-sfc.c @@ -112,7 +112,7 @@ struct hifmc_host { u32 num_chip; }; -static inline int wait_op_finish(struct hifmc_host *host) +static inline int hisi_spi_nor_wait_op_finish(struct hifmc_host *host) { u32 reg; @@ -120,7 +120,7 @@ static inline int wait_op_finish(struct hifmc_host *host) (reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT); } -static int get_if_type(enum spi_nor_protocol proto) +static int hisi_spi_nor_get_if_type(enum spi_nor_protocol proto) { enum hifmc_iftype if_type; @@ -208,7 +208,7 @@ static int hisi_spi_nor_op_reg(struct spi_nor *nor, reg = FMC_OP_CMD1_EN | FMC_OP_REG_OP_START | optype; writel(reg, host->regbase + FMC_OP); - return wait_op_finish(host); + return hisi_spi_nor_wait_op_finish(host); } static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, @@ -259,9 +259,9 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off, reg = OP_CFG_FM_CS(priv->chipselect); if (op_type == FMC_OP_READ) - if_type = get_if_type(nor->read_proto); + if_type = hisi_spi_nor_get_if_type(nor->read_proto); else - if_type = get_if_type(nor->write_proto); + if_type = hisi_spi_nor_get_if_type(nor->write_proto); reg |= OP_CFG_MEM_IF_TYPE(if_type); if (op_type == FMC_OP_READ) reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3); @@ -274,7 +274,7 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off, : OP_CTRL_WR_OPCODE(nor->program_opcode); writel(reg, host->regbase + FMC_OP_DMA); - return wait_op_finish(host); + return hisi_spi_nor_wait_op_finish(host); } static ssize_t hisi_spi_nor_read(struct spi_nor *nor, loff_t from, size_t len, -- cgit v1.2.3 From ce5013ff3bec05cf2a8a05c75fcd520d9914d92b Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 18 Apr 2018 22:25:32 +0200 Subject: mtd: spi-nor: Add support for XM25QH64A and XM25QH128A These devices are produced by Wuhan Xinxin Semiconductor Manufacturing Corp. (XMC) and found on some routers from Chinese manufactures. The data sheets can be found here: http://www.xmcwh.com/Uploads/2018-03-01/5a9799e4cb355.pdf http://www.xmcwh.com/Uploads/2018-02-05/5a77e6dbe968b.pdf Signed-off-by: Hauke Mehrtens Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index cded32974ed1..9363f299e4ee 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1233,6 +1233,10 @@ static const struct flash_info spi_nor_ids[] = { { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) }, { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) }, { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) }, + + /* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */ + { "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { }, }; -- cgit v1.2.3 From f134fbbb4ff813dd227c9ce40b5c0b2078a77b07 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 21 Apr 2018 08:54:40 +1000 Subject: mtd: spi-nor: clear Winbond Extended Address Reg on switch to 3-byte addressing. Winbond spi-nor flash 32MB and larger have an 'Extended Address Register' as one option for addressing beyond 16MB (Macronix has the same concept, Spansion has EXTADD bits in the Bank Address Register). According to section 8.2.7 Write Extended Address Register (C5h) of the Winbond W25Q256FV data sheet (256M-BIT SPI flash) The Extended Address Register is only effective when the device is in the 3-Byte Address Mode. When the device operates in the 4-Byte Address Mode (ADS=1), any command with address input of A31-A24 will replace the Extended Address Register values. It is recommended to check and update the Extended Address Register if necessary when the device is switched from 4-Byte to 3-Byte Address Mode. So the documentation suggests clearing the EAR after switching to 3-byte mode. Experimentation shows that the EAR is *always* one after the switch to 3-byte mode, so clearing the EAR is mandatory at shutdown for a subsequent 3-byte-addressed reboot to work. Note that some SOCs (e.g. MT7621) do not assert a reset line at normal reboot, so we cannot rely on hardware reset. The MT7621 does assert a reset line at watchdog-reset. Acked-by: Marek Vasut Signed-off-by: NeilBrown Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 14 ++++++++++++++ include/linux/mtd/spi-nor.h | 2 ++ 2 files changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 9363f299e4ee..494b7a269872 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -284,6 +284,20 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, if (need_wren) write_disable(nor); + if (!status && !enable && + JEDEC_MFR(info) == SNOR_MFR_WINBOND) { + /* + * On Winbond W25Q256FV, leaving 4byte mode causes + * the Extended Address Register to be set to 1, so all + * 3-byte-address reads come from the second 16M. + * We must clear the register to enable normal behavior. + */ + write_enable(nor); + nor->cmd_buf[0] = 0; + nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1); + write_disable(nor); + } + return status; default: /* Spansion style */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index de36969eb359..e60da0d34cc1 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -62,6 +62,8 @@ #define SPINOR_OP_RDCR 0x35 /* Read configuration register */ #define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ #define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */ +#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */ +#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */ -- cgit v1.2.3 From 7db782bc180efabf6d6628d428be163dc19513ad Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 17 Apr 2018 19:49:14 +0200 Subject: mtd: Remove depends on HAS_DMA in case of platform dependency Remove dependencies on HAS_DMA where a Kconfig symbol depends on another symbol that implies HAS_DMA, and, optionally, on "|| COMPILE_TEST". In most cases this other symbol is an architecture or platform specific symbol, or PCI. Generic symbols and drivers without platform dependencies keep their dependencies on HAS_DMA, to prevent compiling subsystems or drivers that cannot work anyway. This simplifies the dependencies, and allows to improve compile-testing. Signed-off-by: Geert Uytterhoeven Reviewed-by: Mark Brown Acked-by: Robin Murphy Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/Kconfig | 8 ++------ drivers/mtd/spi-nor/Kconfig | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 19a2b283fbbe..6871ff0fd300 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -46,7 +46,7 @@ config MTD_NAND_DENALI config MTD_NAND_DENALI_PCI tristate "Support Denali NAND controller on Intel Moorestown" select MTD_NAND_DENALI - depends on HAS_DMA && PCI + depends on PCI help Enable the driver for NAND flash on Intel Moorestown, using the Denali NAND controller core. @@ -152,7 +152,6 @@ config MTD_NAND_S3C2410_CLKSTOP config MTD_NAND_TANGO tristate "NAND Flash support for Tango chips" depends on ARCH_TANGO || COMPILE_TEST - depends on HAS_DMA help Enables the NAND Flash controller on Tango chips. @@ -285,7 +284,7 @@ config MTD_NAND_MARVELL tristate "NAND controller support on Marvell boards" depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \ COMPILE_TEST - depends on HAS_IOMEM && HAS_DMA + depends on HAS_IOMEM help This enables the NAND flash controller driver for Marvell boards, including: @@ -447,7 +446,6 @@ config MTD_NAND_SH_FLCTL tristate "Support for NAND on Renesas SuperH FLCTL" depends on SUPERH || COMPILE_TEST depends on HAS_IOMEM - depends on HAS_DMA help Several Renesas SuperH CPU has FLCTL. This option enables support for NAND Flash using FLCTL. @@ -515,7 +513,6 @@ config MTD_NAND_SUNXI config MTD_NAND_HISI504 tristate "Support for NAND controller on Hisilicon SoC Hip04" depends on ARCH_HISI || COMPILE_TEST - depends on HAS_DMA help Enables support for NAND controller on Hisilicon SoC Hip04. @@ -529,7 +526,6 @@ config MTD_NAND_QCOM config MTD_NAND_MTK tristate "Support for NAND controller on MTK SoCs" depends on ARCH_MEDIATEK || COMPILE_TEST - depends on HAS_DMA help Enables support for NAND controller on MTK SoCs. This controller is found on mt27xx, mt81xx, mt65xx SoCs. diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 89da88e59121..c493b8230a38 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -71,7 +71,7 @@ config SPI_FSL_QUADSPI config SPI_HISI_SFC tristate "Hisilicon SPI-NOR Flash Controller(SFC)" depends on ARCH_HISI || COMPILE_TEST - depends on HAS_IOMEM && HAS_DMA + depends on HAS_IOMEM help This enables support for hisilicon SPI-NOR flash controller. -- cgit v1.2.3 From 0e210b542cdb34637b849e181178c2d27fcf0d28 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 19 Apr 2018 16:05:59 +0200 Subject: mtd: devices: simplify getting .drvdata We should get drvdata from struct device directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Acked-by: Robert Jarzmik Signed-off-by: Boris Brezillon --- drivers/mtd/devices/docg3.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index c594fe5eac08..802d8f159e90 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1470,8 +1470,7 @@ static struct docg3 *sysfs_dev2docg3(struct device *dev, struct device_attribute *attr) { int floor; - struct platform_device *pdev = to_platform_device(dev); - struct mtd_info **docg3_floors = platform_get_drvdata(pdev); + struct mtd_info **docg3_floors = dev_get_drvdata(dev); floor = attr->attr.name[1] - '0'; if (floor < 0 || floor >= DOC_MAX_NBFLOORS) -- cgit v1.2.3 From c3c9a2c4b94f2c92132298b705a6f5f9dc1ccd3f Mon Sep 17 00:00:00 2001 From: Luca Ellero Date: Wed, 18 Apr 2018 11:26:26 +0200 Subject: mtd: dataflash: replace msleep with usleep_range Since msleep is based on jiffies, this 3 ms sleep becomes actually 20 ms. Worst of all, since this sleep is used in a loop when writing, a single page write (256 to 1024 bytes) causes 17 ms extra time. When writing large files (for example u-boot is usually 512 KB) this delay adds up to minutes. See Documentation/timers/timers-howto.txt "Why not msleep for (1ms - 20ms)". Signed-off-by: Luca Ellero Signed-off-by: Boris Brezillon --- drivers/mtd/devices/mtd_dataflash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index aaaeaae01e1d..3a6f450d1093 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -140,7 +140,7 @@ static int dataflash_waitready(struct spi_device *spi) if (status & (1 << 7)) /* RDY/nBSY */ return status; - msleep(3); + usleep_range(3000, 4000); } } -- cgit v1.2.3 From 7cc9aa669a5119a8d70b22c57779d1decd4d0d62 Mon Sep 17 00:00:00 2001 From: Xiaolei Li Date: Mon, 2 Apr 2018 16:20:10 +0800 Subject: mtd: Add sysfs attribute for mtd OOB available size Expose mtd OOB available size by sysfs file. Then users can get available OOB size by accessing /sys/class/mtd/mtdX/oobavail. Signed-off-by: Xiaolei Li Signed-off-by: Boris Brezillon --- Documentation/ABI/testing/sysfs-class-mtd | 8 ++++++++ drivers/mtd/mtdcore.c | 10 ++++++++++ 2 files changed, 18 insertions(+) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd index f34e592301d1..3bc7c0a95c92 100644 --- a/Documentation/ABI/testing/sysfs-class-mtd +++ b/Documentation/ABI/testing/sysfs-class-mtd @@ -232,3 +232,11 @@ Description: of the parent (another partition or a flash device) in bytes. This attribute is absent on flash devices, so it can be used to distinguish them from partitions. + +What: /sys/class/mtd/mtdX/oobavail +Date: April 2018 +KernelVersion: 4.16 +Contact: linux-mtd@lists.infradead.org +Description: + Number of bytes available for a client to place data into + the out of band area. diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 807d17d863b3..adf18b0d22b5 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -210,6 +210,15 @@ static ssize_t mtd_oobsize_show(struct device *dev, } static DEVICE_ATTR(oobsize, S_IRUGO, mtd_oobsize_show, NULL); +static ssize_t mtd_oobavail_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->oobavail); +} +static DEVICE_ATTR(oobavail, S_IRUGO, mtd_oobavail_show, NULL); + static ssize_t mtd_numeraseregions_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -327,6 +336,7 @@ static struct attribute *mtd_attrs[] = { &dev_attr_writesize.attr, &dev_attr_subpagesize.attr, &dev_attr_oobsize.attr, + &dev_attr_oobavail.attr, &dev_attr_numeraseregions.attr, &dev_attr_name.attr, &dev_attr_ecc_strength.attr, -- cgit v1.2.3 From a8e3923ab57192547ffad01d78939c5c0d5d0c30 Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Fri, 30 Mar 2018 20:00:51 +0530 Subject: mtd: rawnand: davinci: don't acquire and enable clock NAND itself is an asynchronous interface, it does not have any clock input. DaVinci NAND driver acquires clock for AEMIF (asynchronous external memory interface) which is an on-chip IP to which NAND is connected. The same clock is also enabled in AEMIF driver (either present drivers/memory or from machine code for some older platforms). AEMIF timing must be initialized before NAND can be accessed. This ensures that AEMIF clock is enabled too. Remove the superfluous clock acquisition and enable in DaVinci NAND driver. Tested on K2L, K2HK, K2E, DA850 EVM, DA850 LCDK in device-tree boot and DM644x EVM in legacy boot. Signed-off-by: Sekhar Nori Tested-by: Bartosz Golaszewski Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/davinci_nand.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 0f09518d980f..7255a0d94374 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -55,7 +54,6 @@ struct davinci_nand_info { struct nand_chip chip; struct device *dev; - struct clk *clk; bool is_readmode; @@ -703,22 +701,6 @@ static int nand_davinci_probe(struct platform_device *pdev) /* Use board-specific ECC config */ info->chip.ecc.mode = pdata->ecc_mode; - ret = -EINVAL; - - info->clk = devm_clk_get(&pdev->dev, "aemif"); - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(info->clk); - if (ret < 0) { - dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n", - ret); - goto err_clk_enable; - } - spin_lock_irq(&davinci_nand_lock); /* put CSxNAND into NAND mode */ @@ -732,7 +714,7 @@ static int nand_davinci_probe(struct platform_device *pdev) ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); - goto err; + return ret; } switch (info->chip.ecc.mode) { @@ -838,9 +820,6 @@ err_cleanup_nand: nand_cleanup(&info->chip); err: - clk_disable_unprepare(info->clk); - -err_clk_enable: spin_lock_irq(&davinci_nand_lock); if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) ecc4_busy = false; @@ -859,8 +838,6 @@ static int nand_davinci_remove(struct platform_device *pdev) nand_release(nand_to_mtd(&info->chip)); - clk_disable_unprepare(info->clk); - return 0; } -- cgit v1.2.3 From 6bf6ec522e050359b92600f7f820c32181615f87 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 4 Mar 2018 21:06:01 +0100 Subject: mtd: rawnand: gpmi: add support for specific ECC strength Add support for specified ECC strength/size using device tree properties nand-ecc-strength/nand-ecc-step-size. Signed-off-by: Stefan Agner Acked-by: Han Xu Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index c2597c8107a0..abd5146931fa 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -198,17 +198,16 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) * * We may have available oob space in this case. */ -static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) +static int set_geometry_by_ecc_info(struct gpmi_nand_data *this, + unsigned int ecc_strength, + unsigned int ecc_step) { struct bch_geometry *geo = &this->bch_geometry; struct nand_chip *chip = &this->nand; struct mtd_info *mtd = nand_to_mtd(chip); unsigned int block_mark_bit_offset; - if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) - return -EINVAL; - - switch (chip->ecc_step_ds) { + switch (ecc_step) { case SZ_512: geo->gf_len = 13; break; @@ -221,8 +220,8 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) chip->ecc_strength_ds, chip->ecc_step_ds); return -EINVAL; } - geo->ecc_chunk_size = chip->ecc_step_ds; - geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); + geo->ecc_chunk_size = ecc_step; + geo->ecc_strength = round_up(ecc_strength, 2); if (!gpmi_check_ecc(this)) return -EINVAL; @@ -230,7 +229,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) if (geo->ecc_chunk_size < mtd->oobsize) { dev_err(this->dev, "unsupported nand chip. ecc size: %d, oob size : %d\n", - chip->ecc_step_ds, mtd->oobsize); + ecc_step, mtd->oobsize); return -EINVAL; } @@ -423,9 +422,20 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) int common_nfc_set_geometry(struct gpmi_nand_data *this) { + struct nand_chip *chip = &this->nand; + + if (chip->ecc.strength > 0 && chip->ecc.size > 0) + return set_geometry_by_ecc_info(this, chip->ecc.strength, + chip->ecc.size); + if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) - || legacy_set_geometry(this)) - return set_geometry_by_ecc_info(this); + || legacy_set_geometry(this)) { + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return -EINVAL; + + return set_geometry_by_ecc_info(this, chip->ecc_strength_ds, + chip->ecc_step_ds); + } return 0; } -- cgit v1.2.3 From ddd5ed3a90e797e2015cd83e9697c6ed867a3891 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 27 Mar 2018 09:06:14 +0200 Subject: mtd: rawnand: sunxi: Remove support for GPIO-based Ready/Busy polling None of the existing platforms connect the R/B pin to a GPIO (they all use one of the dedicated R/B pin). Anyway, if we ever get short of native R/B pins, it's probably better to fallback to STATUS reg polling than trying to poll a GPIO. Signed-off-by: Boris Brezillon Reviewed-by: Rob Herring --- .../devicetree/bindings/mtd/sunxi-nand.txt | 2 - drivers/mtd/nand/raw/sunxi_nand.c | 91 ++++------------------ 2 files changed, 15 insertions(+), 78 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt index 0734f03bf3d3..dcd5a5d80dc0 100644 --- a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt +++ b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt @@ -22,8 +22,6 @@ Optional properties: - reset : phandle + reset specifier pair - reset-names : must contain "ahb" - allwinner,rb : shall contain the native Ready/Busy ids. - or -- rb-gpios : shall contain the gpios used as R/B pins. - nand-ecc-mode : one of the supported ECC modes ("hw", "soft", "soft_bch" or "none") diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index aad42812a353..d831a141a196 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -165,49 +165,16 @@ #define NFC_MAX_CS 7 -/* - * Ready/Busy detection type: describes the Ready/Busy detection modes - * - * @RB_NONE: no external detection available, rely on STATUS command - * and software timeouts - * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy - * pin of the NAND flash chip must be connected to one of the - * native NAND R/B pins (those which can be muxed to the NAND - * Controller) - * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy - * pin of the NAND flash chip must be connected to a GPIO capable - * pin. - */ -enum sunxi_nand_rb_type { - RB_NONE, - RB_NATIVE, - RB_GPIO, -}; - -/* - * Ready/Busy structure: stores information related to Ready/Busy detection - * - * @type: the Ready/Busy detection mode - * @info: information related to the R/B detection mode. Either a gpio - * id or a native R/B id (those supported by the NAND controller). - */ -struct sunxi_nand_rb { - enum sunxi_nand_rb_type type; - union { - int gpio; - int nativeid; - } info; -}; - /* * Chip Select structure: stores information related to NAND Chip Select * * @cs: the NAND CS id used to communicate with a NAND Chip - * @rb: the Ready/Busy description + * @rb: the Ready/Busy pin ID. -1 means no R/B pin connected to the + * NFC */ struct sunxi_nand_chip_sel { u8 cs; - struct sunxi_nand_rb rb; + s8 rb; }; /* @@ -440,30 +407,19 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd) struct nand_chip *nand = mtd_to_nand(mtd); struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - struct sunxi_nand_rb *rb; - int ret; + u32 mask; if (sunxi_nand->selected < 0) return 0; - rb = &sunxi_nand->sels[sunxi_nand->selected].rb; - - switch (rb->type) { - case RB_NATIVE: - ret = !!(readl(nfc->regs + NFC_REG_ST) & - NFC_RB_STATE(rb->info.nativeid)); - break; - case RB_GPIO: - ret = gpio_get_value(rb->info.gpio); - break; - case RB_NONE: - default: - ret = 0; + if (sunxi_nand->sels[sunxi_nand->selected].rb < 0) { dev_err(nfc->dev, "cannot check R/B NAND status!\n"); - break; + return 0; } - return ret; + mask = NFC_RB_STATE(sunxi_nand->sels[sunxi_nand->selected].rb); + + return !!(readl(nfc->regs + NFC_REG_ST) & mask); } static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) @@ -488,12 +444,11 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | NFC_PAGE_SHIFT(nand->page_shift); - if (sel->rb.type == RB_NONE) { + if (sel->rb < 0) { nand->dev_ready = NULL; } else { nand->dev_ready = sunxi_nfc_dev_ready; - if (sel->rb.type == RB_NATIVE) - ctl |= NFC_RB_SEL(sel->rb.info.nativeid); + ctl |= NFC_RB_SEL(sel->rb); } writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); @@ -1946,26 +1901,10 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, chip->sels[i].cs = tmp; if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) && - tmp < 2) { - chip->sels[i].rb.type = RB_NATIVE; - chip->sels[i].rb.info.nativeid = tmp; - } else { - ret = of_get_named_gpio(np, "rb-gpios", i); - if (ret >= 0) { - tmp = ret; - chip->sels[i].rb.type = RB_GPIO; - chip->sels[i].rb.info.gpio = tmp; - ret = devm_gpio_request(dev, tmp, "nand-rb"); - if (ret) - return ret; - - ret = gpio_direction_input(tmp); - if (ret) - return ret; - } else { - chip->sels[i].rb.type = RB_NONE; - } - } + tmp < 2) + chip->sels[i].rb = tmp; + else + chip->sels[i].rb = -1; } nand = &chip->nand; -- cgit v1.2.3 From 7b4b199459f31541141c4510e542bd4533a60585 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Wed, 11 Apr 2018 10:57:57 +0800 Subject: mtd: rawnand: diskonchip: Replace mdelay with usleep_range in doc_probe doc_probe() is never called in atomic context. doc_probe() is only called by init_nanddoc(), which is only set as a parameter of module_init(). This function is not called in atomic context. Despite never getting called from atomic context, doc_probe() calls mdelay() to busily wait. This is not necessary and can be replaced with usleep_range() to avoid busy waiting. This is found by a static analysis tool named DCNS written by myself. And I also manually check it. Signed-off-by: Jia-Ju Bai Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/diskonchip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c index 86a258de0b75..8d0fb2daad71 100644 --- a/drivers/mtd/nand/raw/diskonchip.c +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -1481,12 +1481,12 @@ static int __init doc_probe(unsigned long physadr) WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); - mdelay(1); + usleep_range(1000, 2000); /* Enable the Millennium Plus ASIC */ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); - mdelay(1); + usleep_range(1000, 2000); ChipID = ReadDOC(virtadr, ChipID); -- cgit v1.2.3 From 36bf2eb90de03d37e05169be29efd2e959ae27a9 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Mon, 16 Apr 2018 10:33:54 +0800 Subject: mtd: rawnand: mtk: use of_device_get_match_data() The usage of of_device_get_match_data() reduce the code size a bit. Also, the only way to call .probe() is to match an entry in .of_match_table[], so of_device_id cannot be NULL. Signed-off-by: Ryder Lee Reviewed-by: Xiaolei Li Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/mtk_ecc.c | 7 +------ drivers/mtd/nand/raw/mtk_nand.c | 10 +--------- 2 files changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c index 40d86a861a70..6432bd70c3b3 100644 --- a/drivers/mtd/nand/raw/mtk_ecc.c +++ b/drivers/mtd/nand/raw/mtk_ecc.c @@ -500,7 +500,6 @@ static int mtk_ecc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mtk_ecc *ecc; struct resource *res; - const struct of_device_id *of_ecc_id = NULL; u32 max_eccdata_size; int irq, ret; @@ -508,11 +507,7 @@ static int mtk_ecc_probe(struct platform_device *pdev) if (!ecc) return -ENOMEM; - of_ecc_id = of_match_device(mtk_ecc_dt_match, &pdev->dev); - if (!of_ecc_id) - return -ENODEV; - - ecc->caps = of_ecc_id->data; + ecc->caps = of_device_get_match_data(dev); max_eccdata_size = ecc->caps->num_ecc_strength - 1; max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size]; diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 6977da3a26aa..75c845adb050 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1434,7 +1434,6 @@ static int mtk_nfc_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct mtk_nfc *nfc; struct resource *res; - const struct of_device_id *of_nfc_id = NULL; int ret, irq; nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); @@ -1452,6 +1451,7 @@ static int mtk_nfc_probe(struct platform_device *pdev) else if (!nfc->ecc) return -ENODEV; + nfc->caps = of_device_get_match_data(dev); nfc->dev = dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1498,14 +1498,6 @@ static int mtk_nfc_probe(struct platform_device *pdev) goto clk_disable; } - of_nfc_id = of_match_device(mtk_nfc_id_table, &pdev->dev); - if (!of_nfc_id) { - ret = -ENODEV; - goto clk_disable; - } - - nfc->caps = of_nfc_id->data; - platform_set_drvdata(pdev, nfc); ret = mtk_nfc_nand_chips_init(dev, nfc); -- cgit v1.2.3 From 87ed67ba65225fa0e23d39f2eaec55d1b2eaa2aa Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 19 Apr 2018 16:06:00 +0200 Subject: mtd: onenand: simplify getting .drvdata We should get drvdata from struct device directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Signed-off-by: Boris Brezillon --- drivers/mtd/nand/onenand/samsung.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/onenand/samsung.c b/drivers/mtd/nand/onenand/samsung.c index 2e9d076e445a..4cce4c0311ca 100644 --- a/drivers/mtd/nand/onenand/samsung.c +++ b/drivers/mtd/nand/onenand/samsung.c @@ -958,8 +958,7 @@ static int s3c_onenand_remove(struct platform_device *pdev) static int s3c_pm_ops_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mtd_info *mtd = platform_get_drvdata(pdev); + struct mtd_info *mtd = dev_get_drvdata(dev); struct onenand_chip *this = mtd->priv; this->wait(mtd, FL_PM_SUSPENDED); @@ -968,8 +967,7 @@ static int s3c_pm_ops_suspend(struct device *dev) static int s3c_pm_ops_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mtd_info *mtd = platform_get_drvdata(pdev); + struct mtd_info *mtd = dev_get_drvdata(dev); struct onenand_chip *this = mtd->priv; this->unlock_all(mtd); -- cgit v1.2.3 From 39b77c586e179db03d8cdbc448d4a0fc22f8b13a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:34 +0200 Subject: mtd: rawnand: fsl_elbc: fix probe function error path An error after nand_scan_tail() should trigger a nand_cleanup(). The helper mtd_device_parse_register() returns an error code that should be checked and nand_cleanup() called accordingly. However, in this driver, fsl_elbc_chip_remove() which is called upon error already triggers a nand_release() which is wrong, because a nand_release() should be triggered only if an mtd_register() succeeded. Move the nand_release() call out of the fsl_elbc_chip_remove() and put it back in the *_remove() hook. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsl_elbc_nand.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index d28df991c73c..51f0b340bc0d 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -813,8 +813,6 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; struct mtd_info *mtd = nand_to_mtd(&priv->chip); - nand_release(mtd); - kfree(mtd->name); if (priv->vbase) @@ -926,15 +924,20 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev) /* First look for RedBoot table or partitions on the command * line, these take precedence over device tree information */ - mtd_device_parse_register(mtd, part_probe_types, NULL, - NULL, 0); + ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); + if (ret) + goto cleanup_nand; pr_info("eLBC NAND device at 0x%llx, bank %d\n", (unsigned long long)res.start, priv->bank); + return 0; +cleanup_nand: + nand_cleanup(&priv->chip); err: fsl_elbc_chip_remove(priv); + return ret; } @@ -942,7 +945,9 @@ static int fsl_elbc_nand_remove(struct platform_device *pdev) { struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev); + struct mtd_info *mtd = nand_to_mtd(&priv->chip); + nand_release(mtd); fsl_elbc_chip_remove(priv); mutex_lock(&fsl_elbc_nand_mutex); -- cgit v1.2.3 From acfc33091f7a5118a7c6d899162e9d9cee72b17a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:35 +0200 Subject: mtd: rawnand: fsl_ifc: fix probe function error path An error after nand_scan_tail() should trigger a nand_cleanup(). The helper mtd_device_parse_register() returns an error code that should be checked and nand_cleanup() called accordingly. However, in this driver, fsl_ifc_chip_remove() which is called upon error already triggers a nand_release() which is wrong, because a nand_release() should be triggered only if an mtd_register() succeeded. Move the nand_release() call out of the fsl_ifc_chip_remove() and put it back in the *_remove() hook. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 61aae0224078..00a609d4473e 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -924,8 +924,6 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) { struct mtd_info *mtd = nand_to_mtd(&priv->chip); - nand_release(mtd); - kfree(mtd->name); if (priv->vbase) @@ -1059,21 +1057,29 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) /* First look for RedBoot table or partitions on the command * line, these take precedence over device tree information */ - mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); + ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); + if (ret) + goto cleanup_nand; dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", (unsigned long long)res.start, priv->bank); + return 0; +cleanup_nand: + nand_cleanup(&priv->chip); err: fsl_ifc_chip_remove(priv); + return ret; } static int fsl_ifc_nand_remove(struct platform_device *dev) { struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); + struct mtd_info *mtd = nand_to_mtd(&priv->chip); + nand_release(mtd); fsl_ifc_chip_remove(priv); mutex_lock(&fsl_ifc_nand_mutex); -- cgit v1.2.3 From 43fab011e93fea0699c2e79a7e561b8f095906f3 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:36 +0200 Subject: mtd: rawnand: fsmc: clean the probe function style Before fixing the error path of the probe function, fix the style of the probe function and mostly the goto labels: they should indicate what the next cleanup is, not the point from which they can be accessed. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsmc_nand.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 28c48dcc514e..35af3890ddba 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -1022,12 +1022,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->read_dma_chan = dma_request_channel(mask, filter, NULL); if (!host->read_dma_chan) { dev_err(&pdev->dev, "Unable to get read dma channel\n"); - goto err_req_read_chnl; + goto disable_clk; } host->write_dma_chan = dma_request_channel(mask, filter, NULL); if (!host->write_dma_chan) { dev_err(&pdev->dev, "Unable to get write dma channel\n"); - goto err_req_write_chnl; + goto release_dma_read_chan; } } @@ -1050,7 +1050,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) ret = nand_scan_ident(mtd, 1, NULL); if (ret) { dev_err(&pdev->dev, "No NAND Device found!\n"); - goto err_scan_ident; + goto release_dma_write_chan; } if (AMBA_REV_BITS(host->pid) >= 8) { @@ -1065,7 +1065,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", mtd->oobsize); ret = -EINVAL; - goto err_probe; + goto release_dma_write_chan; } mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops); @@ -1090,7 +1090,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) default: dev_err(&pdev->dev, "Unsupported ECC mode!\n"); - goto err_probe; + goto release_dma_write_chan; } /* @@ -1110,7 +1110,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) "No oob scheme defined for oobsize %d\n", mtd->oobsize); ret = -EINVAL; - goto err_probe; + goto release_dma_write_chan; } } } @@ -1118,26 +1118,27 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) /* Second stage of scan to fill MTD data-structures */ ret = nand_scan_tail(mtd); if (ret) - goto err_probe; + goto release_dma_write_chan; mtd->name = "nand"; ret = mtd_device_register(mtd, NULL, 0); if (ret) - goto err_probe; + goto release_dma_write_chan; platform_set_drvdata(pdev, host); dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); + return 0; -err_probe: -err_scan_ident: +release_dma_write_chan: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->write_dma_chan); -err_req_write_chnl: +release_dma_read_chan: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->read_dma_chan); -err_req_read_chnl: +disable_clk: clk_disable_unprepare(host->clk); + return ret; } -- cgit v1.2.3 From 682cae27b1da0554496983a356b82b4d87540c48 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:37 +0200 Subject: mtd: rawnand: fsmc: fix the probe function error path An error after nand_scan_tail() should trigger a nand_cleanup(). Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsmc_nand.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 35af3890ddba..f4a5a317d4ae 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -1123,13 +1123,15 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) mtd->name = "nand"; ret = mtd_device_register(mtd, NULL, 0); if (ret) - goto release_dma_write_chan; + goto cleanup_nand; platform_set_drvdata(pdev, host); dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); return 0; +cleanup_nand: + nand_cleanup(nand); release_dma_write_chan: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->write_dma_chan); -- cgit v1.2.3 From 9326dc754c64996ccdc354413674f42486590574 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:38 +0200 Subject: mtd: rawnand: hisi504: clean the probe function error path There is not need for a goto statement when the only action to take is to return. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/hisi504_nand.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index 27558a67fa41..3c64a6281b54 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -731,23 +731,19 @@ static int hisi_nfc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no IRQ resource defined\n"); - ret = -ENXIO; - goto err_res; + return -ENXIO; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->iobase = devm_ioremap_resource(dev, res); - if (IS_ERR(host->iobase)) { - ret = PTR_ERR(host->iobase); - goto err_res; - } + if (IS_ERR(host->iobase)) + return PTR_ERR(host->iobase); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); host->mmio = devm_ioremap_resource(dev, res); if (IS_ERR(host->mmio)) { - ret = PTR_ERR(host->mmio); dev_err(dev, "devm_ioremap_resource[1] fail\n"); - goto err_res; + return PTR_ERR(host->mmio); } mtd->name = "hisi_nand"; @@ -770,19 +766,17 @@ static int hisi_nfc_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host); if (ret) { dev_err(dev, "failed to request IRQ\n"); - goto err_res; + return ret; } ret = nand_scan_ident(mtd, max_chips, NULL); if (ret) - goto err_res; + return ret; host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize, &host->dma_buffer, GFP_KERNEL); - if (!host->buffer) { - ret = -ENOMEM; - goto err_res; - } + if (!host->buffer) + return -ENOMEM; host->dma_oob = host->dma_buffer + mtd->writesize; memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize); @@ -798,8 +792,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) */ default: dev_err(dev, "NON-2KB page size nand flash\n"); - ret = -EINVAL; - goto err_res; + return -EINVAL; } hinfc_write(host, flag, HINFC504_CON); @@ -809,21 +802,17 @@ static int hisi_nfc_probe(struct platform_device *pdev) ret = nand_scan_tail(mtd); if (ret) { dev_err(dev, "nand_scan_tail failed: %d\n", ret); - goto err_res; + return ret; } ret = mtd_device_register(mtd, NULL, 0); if (ret) { dev_err(dev, "Err MTD partition=%d\n", ret); - goto err_mtd; + nand_release(mtd); + return ret; } return 0; - -err_mtd: - nand_release(mtd); -err_res: - return ret; } static int hisi_nfc_remove(struct platform_device *pdev) -- cgit v1.2.3 From 6f533c463105472b71587b9bf8532f34f9d60deb Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:39 +0200 Subject: mtd: rawnand: hisi504: fix the probe function error path An error after nand_scan_tail() should trigger a nand_cleanup() and not a nand_release(). The latter doing an mtd_device_unregister() which is not needed if mtd_device_register() failed. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/hisi504_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index 3c64a6281b54..a1e009c8e556 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -808,7 +808,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) ret = mtd_device_register(mtd, NULL, 0); if (ret) { dev_err(dev, "Err MTD partition=%d\n", ret); - nand_release(mtd); + nand_cleanup(chip); return ret; } -- cgit v1.2.3 From ed64cb1df48152743a927a1187413de35b940441 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:40 +0200 Subject: mtd: rawnand: lpc32xx_mlc: clean the probe function Before fixing the error path of the probe function, fix the style of the entire function and particularly the goto labels: they should indicate what the next cleanup to do is. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_mlc.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index e357948a7505..6e31faf2f07f 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -673,7 +673,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) host->io_base = devm_ioremap_resource(&pdev->dev, rc); if (IS_ERR(host->io_base)) return PTR_ERR(host->io_base); - + host->io_base_phy = rc->start; nand_chip = &host->nand_chip; @@ -706,11 +706,11 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "Clock initialization failure\n"); res = -ENOENT; - goto err_exit1; + goto free_gpio; } res = clk_prepare_enable(host->clk); if (res) - goto err_put_clk; + goto put_clk; nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; nand_chip->dev_ready = lpc32xx_nand_device_ready; @@ -744,7 +744,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = lpc32xx_dma_setup(host); if (res) { res = -EIO; - goto err_exit2; + goto unprepare_clk; } } @@ -754,18 +754,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) */ res = nand_scan_ident(mtd, 1, NULL); if (res) - goto err_exit3; + goto release_dma_chan; host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); if (!host->dma_buf) { res = -ENOMEM; - goto err_exit3; + goto release_dma_chan; } host->dummy_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); if (!host->dummy_buf) { res = -ENOMEM; - goto err_exit3; + goto release_dma_chan; } nand_chip->ecc.mode = NAND_ECC_HW; @@ -783,14 +783,14 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) if (host->irq < 0) { dev_err(&pdev->dev, "failed to get platform irq\n"); res = -EINVAL; - goto err_exit3; + goto release_dma_chan; } if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq, IRQF_TRIGGER_HIGH, DRV_NAME, host)) { dev_err(&pdev->dev, "Error requesting NAND IRQ\n"); res = -ENXIO; - goto err_exit3; + goto release_dma_chan; } /* @@ -799,27 +799,29 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) */ res = nand_scan_tail(mtd); if (res) - goto err_exit4; + goto free_irq; mtd->name = DRV_NAME; res = mtd_device_register(mtd, host->ncfg->parts, host->ncfg->num_parts); - if (!res) - return res; + if (res) + goto release_nand; - nand_release(mtd); + return 0; -err_exit4: +release_nand: + nand_release(mtd); +free_irq: free_irq(host->irq, host); -err_exit3: +release_dma_chan: if (use_dma) dma_release_channel(host->dma_chan); -err_exit2: +unprepare_clk: clk_disable_unprepare(host->clk); -err_put_clk: +put_clk: clk_put(host->clk); -err_exit1: +free_gpio: lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); -- cgit v1.2.3 From 838c07b05b369b3ee6bb18cdc1aec1241a1ef951 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:41 +0200 Subject: mtd: rawnand: lpc32xx_mlc: fix the probe function error path An error after nand_scan_tail() should trigger a nand_cleanup() and not a nand_release(). The latter doing an mtd_device_unregister() which is not needed if mtd_device_register() failed. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_mlc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 6e31faf2f07f..052d123a8304 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -806,12 +806,12 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = mtd_device_register(mtd, host->ncfg->parts, host->ncfg->num_parts); if (res) - goto release_nand; + goto cleanup_nand; return 0; -release_nand: - nand_release(mtd); +cleanup_nand: + nand_cleanup(nand_chip); free_irq: free_irq(host->irq, host); release_dma_chan: -- cgit v1.2.3 From e0ea20bfb46529c9e98165c8135ea6d2c64a2bac Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:42 +0200 Subject: mtd: rawnand: lpc32xx_slc: clean the probe function Before fixing the error path of the probe function, fix the style of the entire function and particularly the goto labels: they should indicate what the next cleanup to do is. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_slc.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 5f7cc6da0a7f..064c9389ee0c 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -831,11 +831,11 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "Clock failure\n"); res = -ENOENT; - goto err_exit1; + goto enable_wp; } res = clk_prepare_enable(host->clk); if (res) - goto err_exit1; + goto enable_wp; /* Set NAND IO addresses and command/ready functions */ chip->IO_ADDR_R = SLC_DATA(host->io_base); @@ -874,19 +874,19 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) GFP_KERNEL); if (host->data_buf == NULL) { res = -ENOMEM; - goto err_exit2; + goto unprepare_clk; } res = lpc32xx_nand_dma_setup(host); if (res) { res = -EIO; - goto err_exit2; + goto unprepare_clk; } /* Find NAND device */ res = nand_scan_ident(mtd, 1, NULL); if (res) - goto err_exit3; + goto release_dma; /* OOB and ECC CPU and DMA work areas */ host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); @@ -920,21 +920,23 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) */ res = nand_scan_tail(mtd); if (res) - goto err_exit3; + goto release_dma; mtd->name = "nxp_lpc3220_slc"; res = mtd_device_register(mtd, host->ncfg->parts, host->ncfg->num_parts); - if (!res) - return res; + if (res) + goto release_nand; - nand_release(mtd); + return 0; -err_exit3: +release_nand: + nand_release(mtd); +release_dma: dma_release_channel(host->dma_chan); -err_exit2: +unprepare_clk: clk_disable_unprepare(host->clk); -err_exit1: +enable_wp: lpc32xx_wp_enable(host); return res; -- cgit v1.2.3 From 553b0c6416e4426d0c8e552ff809c0a590dc3989 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:43 +0200 Subject: mtd: rawnand: lpc32xx_slc: fix the probe function error path An error after nand_scan_tail() should trigger a nand_cleanup() and not a nand_release(). The latter doing an mtd_device_unregister() which is not needed if mtd_device_register() failed. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_slc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 064c9389ee0c..42820aa1abab 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -926,12 +926,12 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = mtd_device_register(mtd, host->ncfg->parts, host->ncfg->num_parts); if (res) - goto release_nand; + goto cleanup_nand; return 0; -release_nand: - nand_release(mtd); +cleanup_nand: + nand_cleanup(chip); release_dma: dma_release_channel(host->dma_chan); unprepare_clk: -- cgit v1.2.3 From 256c4fc76a80a69a5108069d8a09b3836bbf6542 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sun, 22 Apr 2018 18:02:30 +0200 Subject: mtd: rawnand: add a way to pass an ID table with nand_scan() As part of the work of migrating all the drivers to nand_scan(), and because nand_scan() does not provide a way to pass an ID table, rename the function nand_scan_with_ids() and add a third parameter to give a flash ID table (like what was done with nand_scan_ident()). Create a nand_scan() helper that is just a wrapper of nand_scan_with_ids(), passing NULL as the ID table. This way a controller drivers can continue using nand_scan() transparently. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 10 ++++++---- include/linux/mtd/rawnand.h | 9 ++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 72f3a89da513..414a2a3a91d0 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6630,24 +6630,26 @@ EXPORT_SYMBOL(nand_scan_tail); #endif /** - * nand_scan - [NAND Interface] Scan for the NAND device + * nand_scan_with_ids - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @maxchips: number of chips to scan for + * @ids: optional flash IDs table * * This fills out all the uninitialized function pointers with the defaults. * The flash ID is read and the mtd/chip structures are filled with the * appropriate values. */ -int nand_scan(struct mtd_info *mtd, int maxchips) +int nand_scan_with_ids(struct mtd_info *mtd, int maxchips, + struct nand_flash_dev *ids) { int ret; - ret = nand_scan_ident(mtd, maxchips, NULL); + ret = nand_scan_ident(mtd, maxchips, ids); if (!ret) ret = nand_scan_tail(mtd); return ret; } -EXPORT_SYMBOL(nand_scan); +EXPORT_SYMBOL(nand_scan_with_ids); /** * nand_cleanup - [NAND Interface] Free resources held by the NAND device diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 5dad59b31244..ba8d908f5cc7 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -28,7 +28,14 @@ struct nand_flash_dev; struct device_node; /* Scan and identify a NAND device */ -int nand_scan(struct mtd_info *mtd, int max_chips); +int nand_scan_with_ids(struct mtd_info *mtd, int max_chips, + struct nand_flash_dev *ids); + +static inline int nand_scan(struct mtd_info *mtd, int max_chips) +{ + return nand_scan_with_ids(mtd, max_chips, NULL); +} + /* * Separate phases of nand_scan(), allowing board driver to intervene * and override command or ECC setup according to flash type. -- cgit v1.2.3 From c3ee3f3db1facb2b83bba003aacb17c73e710764 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:22 +0200 Subject: mtd: rawnand: gpmi: drop dma_ops_type The GPMI nand driver puts dma_ops_type in its private data struct. Based on the ops type the DMA callback handler unmaps previously mapped buffers. Instead of unmapping the buffers in the DMA callback handler, do this in the caller directly which waits for the DMA transfer to finish. This makes the whole dma_ops_type mechanism unnecessary. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 36 +++++++++++++++++------------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 31 ++----------------------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 11 --------- 3 files changed, 23 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index e94556705dc7..d479358758a0 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -544,19 +544,13 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) return reg & mask; } -static inline void set_dma_type(struct gpmi_nand_data *this, - enum dma_ops_type type) -{ - this->last_dma_type = this->dma_type; - this->dma_type = type; -} - int gpmi_send_command(struct gpmi_nand_data *this) { struct dma_chan *channel = get_dma_chan(this); struct dma_async_tx_descriptor *desc; struct scatterlist *sgl; int chip = this->current_chip; + int ret; u32 pio[3]; /* [1] send out the PIO words */ @@ -586,8 +580,11 @@ int gpmi_send_command(struct gpmi_nand_data *this) return -EINVAL; /* [3] submit the DMA */ - set_dma_type(this, DMA_FOR_COMMAND); - return start_dma_without_bch_irq(this, desc); + ret = start_dma_without_bch_irq(this, desc); + + dma_unmap_sg(this->dev, sgl, 1, DMA_TO_DEVICE); + + return ret; } int gpmi_send_data(struct gpmi_nand_data *this) @@ -595,6 +592,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); int chip = this->current_chip; + int ret; uint32_t command_mode; uint32_t address; u32 pio[2]; @@ -624,8 +622,11 @@ int gpmi_send_data(struct gpmi_nand_data *this) return -EINVAL; /* [3] submit the DMA */ - set_dma_type(this, DMA_FOR_WRITE_DATA); - return start_dma_without_bch_irq(this, desc); + ret = start_dma_without_bch_irq(this, desc); + + dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); + + return ret; } int gpmi_read_data(struct gpmi_nand_data *this) @@ -633,6 +634,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); int chip = this->current_chip; + int ret; u32 pio[2]; /* [1] : send PIO */ @@ -658,8 +660,14 @@ int gpmi_read_data(struct gpmi_nand_data *this) return -EINVAL; /* [3] : submit the DMA */ - set_dma_type(this, DMA_FOR_READ_DATA); - return start_dma_without_bch_irq(this, desc); + + ret = start_dma_without_bch_irq(this, desc); + + dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); + if (this->direct_dma_map_ok == false) + memcpy(this->upper_buf, this->data_buffer_dma, this->upper_len); + + return ret; } int gpmi_send_page(struct gpmi_nand_data *this, @@ -703,7 +711,6 @@ int gpmi_send_page(struct gpmi_nand_data *this, if (!desc) return -EINVAL; - set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE); return start_dma_with_bch_irq(this, desc); } @@ -785,7 +792,6 @@ int gpmi_read_page(struct gpmi_nand_data *this, return -EINVAL; /* [4] submit the DMA */ - set_dma_type(this, DMA_FOR_READ_ECC_PAGE); return start_dma_with_bch_irq(this, desc); } diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index abd5146931fa..6252776b32ca 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -482,31 +482,6 @@ static void dma_irq_callback(void *param) struct gpmi_nand_data *this = param; struct completion *dma_c = &this->dma_done; - switch (this->dma_type) { - case DMA_FOR_COMMAND: - dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE); - break; - - case DMA_FOR_READ_DATA: - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); - if (this->direct_dma_map_ok == false) - memcpy(this->upper_buf, this->data_buffer_dma, - this->upper_len); - break; - - case DMA_FOR_WRITE_DATA: - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); - break; - - case DMA_FOR_READ_ECC_PAGE: - case DMA_FOR_WRITE_ECC_PAGE: - /* We have to wait the BCH interrupt to finish. */ - break; - - default: - dev_err(this->dev, "in wrong DMA operation.\n"); - } - complete(dma_c); } @@ -526,8 +501,7 @@ int start_dma_without_bch_irq(struct gpmi_nand_data *this, /* Wait for the interrupt from the DMA block. */ timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000)); if (!timeout) { - dev_err(this->dev, "DMA timeout, last DMA :%d\n", - this->last_dma_type); + dev_err(this->dev, "DMA timeout, last DMA\n"); gpmi_dump_info(this); return -ETIMEDOUT; } @@ -556,8 +530,7 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *this, /* Wait for the interrupt from the BCH block. */ timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000)); if (!timeout) { - dev_err(this->dev, "BCH timeout, last DMA :%d\n", - this->last_dma_type); + dev_err(this->dev, "BCH timeout\n"); gpmi_dump_info(this); return -ETIMEDOUT; } diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 62fde59b995f..2397010a8963 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -77,15 +77,6 @@ struct boot_rom_geometry { unsigned int search_area_stride_exponent; }; -/* DMA operations types */ -enum dma_ops_type { - DMA_FOR_COMMAND = 1, - DMA_FOR_READ_DATA, - DMA_FOR_WRITE_DATA, - DMA_FOR_READ_ECC_PAGE, - DMA_FOR_WRITE_ECC_PAGE -}; - enum gpmi_type { IS_MX23, IS_MX28, @@ -178,8 +169,6 @@ struct gpmi_nand_data { /* DMA channels */ #define DMA_CHANS 8 struct dma_chan *dma_chans[DMA_CHANS]; - enum dma_ops_type last_dma_type; - enum dma_ops_type dma_type; struct completion dma_done; /* private */ -- cgit v1.2.3 From ba3900e6fac59978d379982e843876183298f28a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:23 +0200 Subject: mtd: rawnand: gpmi: pass buffer and len around Instead of putting the buffer and len passed in from the mtd core into the private data struct, just pass it around in the GPMI drivers functions. This makes the lifetime of the variables more clear and the code easier to follow. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 14 +++++++------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 20 ++++++++------------ drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 11 ++++------- 3 files changed, 19 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index d479358758a0..447961b798b4 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -587,7 +587,7 @@ int gpmi_send_command(struct gpmi_nand_data *this) return ret; } -int gpmi_send_data(struct gpmi_nand_data *this) +int gpmi_send_data(struct gpmi_nand_data *this, const void *buf, int len) { struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); @@ -606,7 +606,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) | BF_GPMI_CTRL0_CS(chip, this) | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); + | BF_GPMI_CTRL0_XFER_COUNT(len); pio[1] = 0; desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); @@ -614,7 +614,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) return -EINVAL; /* [2] send DMA request */ - prepare_data_dma(this, DMA_TO_DEVICE); + prepare_data_dma(this, buf, len, DMA_TO_DEVICE); desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -629,7 +629,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) return ret; } -int gpmi_read_data(struct gpmi_nand_data *this) +int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len) { struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); @@ -643,7 +643,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) | BF_GPMI_CTRL0_CS(chip, this) | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) - | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); + | BF_GPMI_CTRL0_XFER_COUNT(len); pio[1] = 0; desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, @@ -652,7 +652,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) return -EINVAL; /* [2] : send DMA request */ - prepare_data_dma(this, DMA_FROM_DEVICE); + prepare_data_dma(this, buf, len, DMA_FROM_DEVICE); desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -665,7 +665,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); if (this->direct_dma_map_ok == false) - memcpy(this->upper_buf, this->data_buffer_dma, this->upper_len); + memcpy(buf, this->data_buffer_dma, len); return ret; } diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 6252776b32ca..c66712e594f7 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -447,15 +447,15 @@ struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) } /* Can we use the upper's buffer directly for DMA? */ -void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) +void prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, + enum dma_data_direction dr) { struct scatterlist *sgl = &this->data_sgl; int ret; /* first try to map the upper buffer directly */ - if (virt_addr_valid(this->upper_buf) && - !object_is_on_stack(this->upper_buf)) { - sg_init_one(sgl, this->upper_buf, this->upper_len); + if (virt_addr_valid(buf) && !object_is_on_stack(buf)) { + sg_init_one(sgl, buf, len); ret = dma_map_sg(this->dev, sgl, 1, dr); if (ret == 0) goto map_fail; @@ -466,10 +466,10 @@ void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) map_fail: /* We have to use our own DMA buffer. */ - sg_init_one(sgl, this->data_buffer_dma, this->upper_len); + sg_init_one(sgl, this->data_buffer_dma, len); if (dr == DMA_TO_DEVICE) - memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len); + memcpy(this->data_buffer_dma, buf, len); dma_map_sg(this->dev, sgl, 1, dr); @@ -929,10 +929,8 @@ static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) struct gpmi_nand_data *this = nand_get_controller_data(chip); dev_dbg(this->dev, "len is %d\n", len); - this->upper_buf = buf; - this->upper_len = len; - gpmi_read_data(this); + gpmi_read_data(this, buf, len); } static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) @@ -941,10 +939,8 @@ static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) struct gpmi_nand_data *this = nand_get_controller_data(chip); dev_dbg(this->dev, "len is %d\n", len); - this->upper_buf = (uint8_t *)buf; - this->upper_len = len; - gpmi_send_data(this); + gpmi_send_data(this, buf, len); } static uint8_t gpmi_read_byte(struct mtd_info *mtd) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 2397010a8963..fba72ad28263 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -141,10 +141,6 @@ struct gpmi_nand_data { int current_chip; unsigned int command_length; - /* passed from upper layer */ - uint8_t *upper_buf; - int upper_len; - /* for DMA operations */ bool direct_dma_map_ok; @@ -178,7 +174,7 @@ struct gpmi_nand_data { /* Common Services */ int common_nfc_set_geometry(struct gpmi_nand_data *); struct dma_chan *get_dma_chan(struct gpmi_nand_data *); -void prepare_data_dma(struct gpmi_nand_data *, +void prepare_data_dma(struct gpmi_nand_data *, const void *buf, int len, enum dma_data_direction dr); int start_dma_without_bch_irq(struct gpmi_nand_data *, struct dma_async_tx_descriptor *); @@ -197,8 +193,9 @@ int gpmi_disable_clk(struct gpmi_nand_data *this); int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr, const struct nand_data_interface *conf); void gpmi_nfc_apply_timings(struct gpmi_nand_data *this); -int gpmi_read_data(struct gpmi_nand_data *); -int gpmi_send_data(struct gpmi_nand_data *); +int gpmi_read_data(struct gpmi_nand_data *, void *buf, int len); +int gpmi_send_data(struct gpmi_nand_data *, const void *buf, int len); + int gpmi_send_page(struct gpmi_nand_data *, dma_addr_t payload, dma_addr_t auxiliary); int gpmi_read_page(struct gpmi_nand_data *, -- cgit v1.2.3 From 118c3f9b01629bdfb13c07efdcb34f8818cce3fa Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:24 +0200 Subject: mtd: rawnand: gpmi: put only once used functions inline read_page_prepare(), read_page_end() and read_page_swap_end() are trivial functions that are used only once and take 8 arguments each. De-obfuscate the code by open coding these functions in gpmi_ecc_read_page() Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 89 ++++++++---------------------- 1 file changed, 23 insertions(+), 66 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index c66712e594f7..82782e4d8eab 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -678,56 +678,6 @@ static void release_resources(struct gpmi_nand_data *this) release_dma_channels(this); } -static int read_page_prepare(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void **use_virt, dma_addr_t *use_phys) -{ - struct device *dev = this->dev; - - if (virt_addr_valid(destination)) { - dma_addr_t dest_phys; - - dest_phys = dma_map_single(dev, destination, - length, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, dest_phys)) { - if (alt_size < length) { - dev_err(dev, "Alternate buffer is too small\n"); - return -ENOMEM; - } - goto map_failed; - } - *use_virt = destination; - *use_phys = dest_phys; - this->direct_dma_map_ok = true; - return 0; - } - -map_failed: - *use_virt = alt_virt; - *use_phys = alt_phys; - this->direct_dma_map_ok = false; - return 0; -} - -static inline void read_page_end(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void *used_virt, dma_addr_t used_phys) -{ - if (this->direct_dma_map_ok) - dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE); -} - -static inline void read_page_swap_end(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void *used_virt, dma_addr_t used_phys) -{ - if (!this->direct_dma_map_ok) - memcpy(destination, alt_virt, length); -} - static int send_page_prepare(struct gpmi_nand_data *this, const void *source, unsigned length, void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, @@ -1018,24 +968,33 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, int ret; dev_dbg(this->dev, "page number is : %d\n", page); - ret = read_page_prepare(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - &payload_virt, &payload_phys); - if (ret) { - dev_err(this->dev, "Inadequate DMA buffer\n"); - ret = -ENOMEM; - return ret; + + payload_virt = this->payload_virt; + payload_phys = this->payload_phys; + this->direct_dma_map_ok = false; + + if (virt_addr_valid(buf)) { + dma_addr_t dest_phys; + + dest_phys = dma_map_single(this->dev, buf, nfc_geo->payload_size, + DMA_FROM_DEVICE); + if (!dma_mapping_error(this->dev, dest_phys)) { + payload_virt = buf; + payload_phys = dest_phys; + this->direct_dma_map_ok = true; + } } + auxiliary_virt = this->auxiliary_virt; auxiliary_phys = this->auxiliary_phys; /* go! */ ret = gpmi_read_page(this, payload_phys, auxiliary_phys); - read_page_end(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); + + if (this->direct_dma_map_ok) + dma_unmap_single(this->dev, payload_phys, nfc_geo->payload_size, + DMA_FROM_DEVICE); + if (ret) { dev_err(this->dev, "Error in ECC-based read: %d\n", ret); return ret; @@ -1044,10 +1003,8 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, /* Loop over status bytes, accumulating ECC status. */ status = auxiliary_virt + nfc_geo->auxiliary_status_offset; - read_page_swap_end(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); + if (!this->direct_dma_map_ok) + memcpy(buf, this->payload_virt, nfc_geo->payload_size); for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) -- cgit v1.2.3 From 111bfed4f30ff16e1d47e4ce3a1f427882f365d3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:25 +0200 Subject: mtd: rawnand: gpmi: remove direct_dma_map_ok from driver data struct Instead of putting direct_dma_map_ok into driver struct pass it around between functions to make the code more readable. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 5 +++-- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 15 +++++++-------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 5 +---- 3 files changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index 447961b798b4..39834bedf460 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -636,6 +636,7 @@ int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len) int chip = this->current_chip; int ret; u32 pio[2]; + bool direct; /* [1] : send PIO */ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) @@ -652,7 +653,7 @@ int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len) return -EINVAL; /* [2] : send DMA request */ - prepare_data_dma(this, buf, len, DMA_FROM_DEVICE); + direct = prepare_data_dma(this, buf, len, DMA_FROM_DEVICE); desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -664,7 +665,7 @@ int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len) ret = start_dma_without_bch_irq(this, desc); dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); - if (this->direct_dma_map_ok == false) + if (!direct) memcpy(buf, this->data_buffer_dma, len); return ret; diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 82782e4d8eab..148faee6a543 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -447,7 +447,7 @@ struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) } /* Can we use the upper's buffer directly for DMA? */ -void prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, +bool prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, enum dma_data_direction dr) { struct scatterlist *sgl = &this->data_sgl; @@ -460,8 +460,7 @@ void prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, if (ret == 0) goto map_fail; - this->direct_dma_map_ok = true; - return; + return true; } map_fail: @@ -473,7 +472,7 @@ map_fail: dma_map_sg(this->dev, sgl, 1, dr); - this->direct_dma_map_ok = false; + return false; } /* This will be called after the DMA operation is finished. */ @@ -966,12 +965,12 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, unsigned char *status; unsigned int max_bitflips = 0; int ret; + bool direct = false; dev_dbg(this->dev, "page number is : %d\n", page); payload_virt = this->payload_virt; payload_phys = this->payload_phys; - this->direct_dma_map_ok = false; if (virt_addr_valid(buf)) { dma_addr_t dest_phys; @@ -981,7 +980,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, if (!dma_mapping_error(this->dev, dest_phys)) { payload_virt = buf; payload_phys = dest_phys; - this->direct_dma_map_ok = true; + direct = true; } } @@ -991,7 +990,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, /* go! */ ret = gpmi_read_page(this, payload_phys, auxiliary_phys); - if (this->direct_dma_map_ok) + if (direct) dma_unmap_single(this->dev, payload_phys, nfc_geo->payload_size, DMA_FROM_DEVICE); @@ -1003,7 +1002,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, /* Loop over status bytes, accumulating ECC status. */ status = auxiliary_virt + nfc_geo->auxiliary_status_offset; - if (!this->direct_dma_map_ok) + if (!direct) memcpy(buf, this->payload_virt, nfc_geo->payload_size); for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index fba72ad28263..6aa10d6962d6 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -141,9 +141,6 @@ struct gpmi_nand_data { int current_chip; unsigned int command_length; - /* for DMA operations */ - bool direct_dma_map_ok; - struct scatterlist cmd_sgl; char *cmd_buffer; @@ -174,7 +171,7 @@ struct gpmi_nand_data { /* Common Services */ int common_nfc_set_geometry(struct gpmi_nand_data *); struct dma_chan *get_dma_chan(struct gpmi_nand_data *); -void prepare_data_dma(struct gpmi_nand_data *, const void *buf, int len, +bool prepare_data_dma(struct gpmi_nand_data *, const void *buf, int len, enum dma_data_direction dr); int start_dma_without_bch_irq(struct gpmi_nand_data *, struct dma_async_tx_descriptor *); -- cgit v1.2.3 From e637f5fe8e0cbee455b75afbc0cf2a0cc76997f4 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:26 +0200 Subject: mtd: rawnand: gpmi: return valid value from bch_set_geometry() The caller of bch_set_geometry() expects the return value to be an error code, so !0 is not valid. return the error from the just called function instead. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index 39834bedf460..83697b8df871 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -258,8 +258,9 @@ int bch_set_geometry(struct gpmi_nand_data *this) unsigned int gf_len; int ret; - if (common_nfc_set_geometry(this)) - return !0; + ret = common_nfc_set_geometry(this); + if (ret) + return ret; block_count = bch_geo->ecc_chunk_count - 1; block_size = bch_geo->ecc_chunk_size; -- cgit v1.2.3 From f6b74db84cd1ab7c447e412a4b83f9933a68589b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:27 +0200 Subject: mtd: rawnand: gpmi: remove unnecessary variables Use this->auxiliary_virt and this->auxiliary_phys directly rather than creating extra local variables for them. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 148faee6a543..f6aa358a3452 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -959,8 +959,6 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, struct mtd_info *mtd = nand_to_mtd(chip); void *payload_virt; dma_addr_t payload_phys; - void *auxiliary_virt; - dma_addr_t auxiliary_phys; unsigned int i; unsigned char *status; unsigned int max_bitflips = 0; @@ -984,11 +982,8 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, } } - auxiliary_virt = this->auxiliary_virt; - auxiliary_phys = this->auxiliary_phys; - /* go! */ - ret = gpmi_read_page(this, payload_phys, auxiliary_phys); + ret = gpmi_read_page(this, payload_phys, this->auxiliary_phys); if (direct) dma_unmap_single(this->dev, payload_phys, nfc_geo->payload_size, @@ -1000,7 +995,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, } /* Loop over status bytes, accumulating ECC status. */ - status = auxiliary_virt + nfc_geo->auxiliary_status_offset; + status = this->auxiliary_virt + nfc_geo->auxiliary_status_offset; if (!direct) memcpy(buf, this->payload_virt, nfc_geo->payload_size); @@ -1058,7 +1053,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, buf + i * nfc_geo->ecc_chunk_size, nfc_geo->ecc_chunk_size, eccbuf, eccbytes, - auxiliary_virt, + this->auxiliary_virt, nfc_geo->metadata_size, nfc_geo->ecc_strength); } else { @@ -1086,7 +1081,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, } /* handle the block mark swapping */ - block_mark_swapping(this, buf, auxiliary_virt); + block_mark_swapping(this, buf, this->auxiliary_virt); if (oob_required) { /* @@ -1100,7 +1095,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, * the block mark. */ memset(chip->oob_poi, ~0, mtd->oobsize); - chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + chip->oob_poi[0] = ((uint8_t *)this->auxiliary_virt)[0]; } return max_bitflips; -- cgit v1.2.3 From 27ab41e2c183e960a045c8f3b87b2341a5f10f19 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sun, 29 Apr 2018 08:00:53 -0700 Subject: mtd: nftl: Remove VLA usage On the quest to remove all stack VLAs from the kernel[1] this changes the check_free_sectors() routine to use a kmalloc()ed buffer instead of a large VLA stack buffer. [1] https://lkml.kernel.org/r/CA+55aFzCG-zNmZwX4A2FQpadafLfEzK6CC=qPXydAacU1RqZWA@mail.gmail.com Signed-off-by: Kees Cook Signed-off-by: Boris Brezillon --- drivers/mtd/inftlmount.c | 23 ++++++++++++++++------- drivers/mtd/nftlmount.c | 23 ++++++++++++++++------- 2 files changed, 32 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index aab4f68bd36f..2d598412972d 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -334,28 +334,37 @@ static int memcmpb(void *a, int c, int n) static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, int len, int check_oob) { - u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize]; struct mtd_info *mtd = inftl->mbd.mtd; size_t retlen; - int i; + int i, ret; + u8 *buf; + + buf = kmalloc(SECTORSIZE + mtd->oobsize, GFP_KERNEL); + if (!buf) + return -1; + ret = -1; for (i = 0; i < len; i += SECTORSIZE) { if (mtd_read(mtd, address, SECTORSIZE, &retlen, buf)) - return -1; + goto out; if (memcmpb(buf, 0xff, SECTORSIZE) != 0) - return -1; + goto out; if (check_oob) { if(inftl_read_oob(mtd, address, mtd->oobsize, &retlen, &buf[SECTORSIZE]) < 0) - return -1; + goto out; if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) - return -1; + goto out; } address += SECTORSIZE; } - return 0; + ret = 0; + +out: + kfree(buf); + return ret; } /* diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index a6fbfa4e5799..6281da3dadac 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -272,28 +272,37 @@ static int memcmpb(void *a, int c, int n) static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len, int check_oob) { - u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize]; struct mtd_info *mtd = nftl->mbd.mtd; size_t retlen; - int i; + int i, ret; + u8 *buf; + + buf = kmalloc(SECTORSIZE + mtd->oobsize, GFP_KERNEL); + if (!buf) + return -1; + ret = -1; for (i = 0; i < len; i += SECTORSIZE) { if (mtd_read(mtd, address, SECTORSIZE, &retlen, buf)) - return -1; + goto out; if (memcmpb(buf, 0xff, SECTORSIZE) != 0) - return -1; + goto out; if (check_oob) { if(nftl_read_oob(mtd, address, mtd->oobsize, &retlen, &buf[SECTORSIZE]) < 0) - return -1; + goto out; if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) - return -1; + goto out; } address += SECTORSIZE; } - return 0; + ret = 0; + +out: + kfree(buf); + return ret; } /* NFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase Unit and -- cgit v1.2.3 From 051529d0c010a07c84fb42626053e4df610857c5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 23 Apr 2018 09:07:13 -0300 Subject: mtd: maps: pismo: Remove owner assignment from i2c_driver Structure i2c_driver does not need to set the owner field, as this will be populated by the driver core. Generated by scripts/coccinelle/api/platform_no_drv_owner.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Boris Brezillon --- drivers/mtd/maps/pismo.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/pismo.c b/drivers/mtd/maps/pismo.c index dc6df9abea0b..c065d7995c0a 100644 --- a/drivers/mtd/maps/pismo.c +++ b/drivers/mtd/maps/pismo.c @@ -265,7 +265,6 @@ MODULE_DEVICE_TABLE(i2c, pismo_id); static struct i2c_driver pismo_driver = { .driver = { .name = "pismo", - .owner = THIS_MODULE, }, .probe = pismo_probe, .remove = pismo_remove, -- cgit v1.2.3 From b1c97e2335f24dbdcc26cfdc882b8b5a0bad25c2 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Mon, 30 Apr 2018 11:34:49 -0500 Subject: mtd: cfi: Support early CFI fixups Some CFI devices need fixups that affect the number of chips detected, but the current fixup infrastructure (struct cfi_fixup and cfi_fixup()) does not cover this situation. Introduce struct cfi_early_fixup and cfi_early_fixup() to fill the void. Signed-off-by: Aaron Sierra Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_probe.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index e8d0164498b0..812df70ab6a6 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -63,6 +63,30 @@ do { \ #endif +/* + * This fixup occurs immediately after reading the CFI structure and can affect + * the number of chips detected, unlike cfi_fixup, which occurs after an + * mtd_info structure has been created for the chip. + */ +struct cfi_early_fixup { + uint16_t mfr; + uint16_t id; + void (*fixup)(struct cfi_private *cfi); +}; + +static void cfi_early_fixup(struct cfi_private *cfi, + const struct cfi_early_fixup *fixups) +{ + const struct cfi_early_fixup *f; + + for (f = fixups; f->fixup; f++) { + if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && + ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { + f->fixup(cfi); + } + } +} + /* check for QRY. in: interleave,type,mode ret: table index, <0 for error @@ -151,6 +175,10 @@ static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, return 1; } +static const struct cfi_early_fixup cfi_early_fixup_table[] = { + { }, +}; + static int __xipram cfi_chip_setup(struct map_info *map, struct cfi_private *cfi) { @@ -235,6 +263,8 @@ static int __xipram cfi_chip_setup(struct map_info *map, cfi_qry_mode_off(base, map, cfi); xip_allowed(base, map); + cfi_early_fixup(cfi, cfi_early_fixup_table); + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank. Manufacturer ID %#08x Chip ID %#08x\n", map->name, cfi->interleave, cfi->device_type*8, base, map->bankwidth*8, cfi->mfr, cfi->id); -- cgit v1.2.3 From 0fe3ede7941dfcd6fd540fc2b086453d3ee8f236 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Mon, 30 Apr 2018 11:35:07 -0500 Subject: mtd: cfi: Add early fixup for S70GL02GS S70GL02GS flash reports a single 256 MiB chip, but is really made up of two 128 MiB chips with 1024 sectors each. Without early fixups (top half of device cannot be written or erased): ff0000000.nor-boot: Found 1 x16 devices at 0x0 in 16-bit bank. Amd/Fujitsu Extended Query Table at 0x0040 Amd/Fujitsu Extended Query version 1.5. number of CFI chips: 1 With early fixups (entire device can be written and erased): Bad S70GL02GS CFI data; adjust to detect 2 chips ff0000000.nor-boot: Found 1 x16 devices at 0x0 in 16-bit bank. ff0000000.nor-boot: Found 1 x16 devices at 0x8000000 in 16-bit bank Amd/Fujitsu Extended Query Table at 0x0040 Amd/Fujitsu Extended Query version 1.5. number of CFI chips: 2 Signed-off-by: Aaron Sierra Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_probe.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index 812df70ab6a6..cf426956454c 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -175,7 +175,19 @@ static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, return 1; } +static void fixup_s70gl02gs_chips(struct cfi_private *cfi) +{ + /* + * S70GL02GS flash reports a single 256 MiB chip, but is really made up + * of two 128 MiB chips with 1024 sectors each. + */ + cfi->cfiq->DevSize = 27; + cfi->cfiq->EraseRegionInfo[0] = 0x20003ff; + pr_warn("Bad S70GL02GS CFI data; adjust to detect 2 chips\n"); +} + static const struct cfi_early_fixup cfi_early_fixup_table[] = { + { CFI_MFR_AMD, 0x4801, fixup_s70gl02gs_chips }, { }, }; -- cgit v1.2.3 From 5ac67ce36cfe38b4c104a42ce52c5c8d526f1c95 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 27 Mar 2018 22:35:41 +0200 Subject: mtd: move code adding (registering) partitions to the parse_mtd_partitions() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit slightly simplifies the code. Every parse_mtd_partitions() caller (out of two existing ones) had to add partitions & cleanup parser on its own. This moves that responsibility into the function. That change also allows dropping struct mtd_partitions argument. There is one minor behavior change caused by this cleanup. If parse_mtd_partitions() fails to add partitions (add_mtd_partitions() return an error) then mtd_device_parse_register() will still try to add (register) fallback partitions. It's a real corner case affecting one of uncommon error paths and shouldn't cause any harm. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 14 ++++---------- drivers/mtd/mtdcore.h | 1 - drivers/mtd/mtdpart.c | 44 ++++++++++++++++---------------------------- 3 files changed, 20 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index adf18b0d22b5..7b0d105627b1 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -700,7 +700,6 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, const struct mtd_partition *parts, int nr_parts) { - struct mtd_partitions parsed = { }; int ret; mtd_set_dev_defaults(mtd); @@ -712,13 +711,10 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, } /* Prefer parsed partitions over driver-provided fallback */ - ret = parse_mtd_partitions(mtd, types, &parsed, parser_data); - if (!ret && parsed.nr_parts) { - parts = parsed.parts; - nr_parts = parsed.nr_parts; - } - - if (nr_parts) + ret = parse_mtd_partitions(mtd, types, parser_data); + if (ret > 0) + ret = 0; + else if (nr_parts) ret = add_mtd_partitions(mtd, parts, nr_parts); else if (!device_is_registered(&mtd->dev)) ret = add_mtd_device(mtd); @@ -744,8 +740,6 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, } out: - /* Cleanup any parsed partitions */ - mtd_part_parser_cleanup(&parsed); if (ret && device_is_registered(&mtd->dev)) del_mtd_device(mtd); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index 37accfd0400e..9887bda317cd 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -15,7 +15,6 @@ int del_mtd_partitions(struct mtd_info *); struct mtd_partitions; int parse_mtd_partitions(struct mtd_info *master, const char * const *types, - struct mtd_partitions *pparts, struct mtd_part_parser_data *data); void mtd_part_parser_cleanup(struct mtd_partitions *parts); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 023516a63276..f8d3a015cdad 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -335,20 +335,7 @@ static inline void free_partition(struct mtd_part *p) */ static int mtd_parse_part(struct mtd_part *slave, const char *const *types) { - struct mtd_partitions parsed; - int err; - - err = parse_mtd_partitions(&slave->mtd, types, &parsed, NULL); - if (err) - return err; - else if (!parsed.nr_parts) - return -ENOENT; - - err = add_mtd_partitions(&slave->mtd, parsed.parts, parsed.nr_parts); - - mtd_part_parser_cleanup(&parsed); - - return err; + return parse_mtd_partitions(&slave->mtd, types, NULL); } static struct mtd_part *allocate_partition(struct mtd_info *parent, @@ -933,30 +920,27 @@ static int mtd_part_of_parse(struct mtd_info *master, } /** - * parse_mtd_partitions - parse MTD partitions + * parse_mtd_partitions - parse and register MTD partitions + * * @master: the master partition (describes whole MTD device) * @types: names of partition parsers to try or %NULL - * @pparts: info about partitions found is returned here * @data: MTD partition parser-specific data * - * This function tries to find partition on MTD device @master. It uses MTD - * partition parsers, specified in @types. However, if @types is %NULL, then - * the default list of parsers is used. The default list contains only the + * This function tries to find & register partitions on MTD device @master. It + * uses MTD partition parsers, specified in @types. However, if @types is %NULL, + * then the default list of parsers is used. The default list contains only the * "cmdlinepart" and "ofpart" parsers ATM. * Note: If there are more then one parser in @types, the kernel only takes the * partitions parsed out by the first parser. * * This function may return: * o a negative error code in case of failure - * o zero otherwise, and @pparts will describe the partitions, number of - * partitions, and the parser which parsed them. Caller must release - * resources with mtd_part_parser_cleanup() when finished with the returned - * data. + * o number of found partitions otherwise */ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, - struct mtd_partitions *pparts, struct mtd_part_parser_data *data) { + struct mtd_partitions pparts = { }; struct mtd_part_parser *parser; int ret, err = 0; @@ -970,7 +954,7 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, * handled in a separated function. */ if (!strcmp(*types, "ofpart")) { - ret = mtd_part_of_parse(master, pparts); + ret = mtd_part_of_parse(master, &pparts); } else { pr_debug("%s: parsing partitions %s\n", master->name, *types); @@ -981,13 +965,17 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, parser ? parser->name : NULL); if (!parser) continue; - ret = mtd_part_do_parse(parser, master, pparts, data); + ret = mtd_part_do_parse(parser, master, &pparts, data); if (ret <= 0) mtd_part_parser_put(parser); } /* Found partitions! */ - if (ret > 0) - return 0; + if (ret > 0) { + err = add_mtd_partitions(master, pparts.parts, + pparts.nr_parts); + mtd_part_parser_cleanup(&pparts); + return err ? err : pparts.nr_parts; + } /* * Stash the first error we see; only report it if no parser * succeeds -- cgit v1.2.3 From a75bbe71a27875fdc61cde1af6d799037cef6bed Mon Sep 17 00:00:00 2001 From: Jane Wan Date: Tue, 8 May 2018 14:19:53 -0700 Subject: mtd: rawnand: fsl_ifc: fix FSL NAND driver to read all ONFI parameter pages Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page read is not valid, the host should read redundant parameter page copies. Fix FSL NAND driver to read the two redundant copies which are mandatory in the specification. Signed-off-by: Jane Wan Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 00a609d4473e..382b67e97174 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, case NAND_CMD_READID: case NAND_CMD_PARAM: { + /* + * For READID, read 8 bytes that are currently used. + * For PARAM, read all 3 copies of 256-bytes pages. + */ + int len = 8; int timing = IFC_FIR_OP_RB; - if (command == NAND_CMD_PARAM) + if (command == NAND_CMD_PARAM) { timing = IFC_FIR_OP_RBCD; + len = 256 * 3; + } ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | @@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, &ifc->ifc_nand.nand_fcr0); ifc_out32(column, &ifc->ifc_nand.row3); - /* - * although currently it's 8 bytes for READID, we always read - * the maximum 256 bytes(for PARAM) - */ - ifc_out32(256, &ifc->ifc_nand.nand_fbcr); - ifc_nand_ctrl->read_bytes = 256; + ifc_out32(len, &ifc->ifc_nand.nand_fbcr); + ifc_nand_ctrl->read_bytes = len; set_addr(mtd, 0, 0, 0); fsl_ifc_run_command(mtd); -- cgit v1.2.3 From 237ea0d4762cc14d0fc80e80d61f0f08e1050c7f Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 12 Apr 2018 07:24:52 +0200 Subject: mtd: bcm47xxpart: improve handling TRX partition size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When bcm47xxpart finds a TRX partition (container) it's supposed to jump to the end of it and keep looking for more partitions. TRX and its subpartitions are handled by a separate parser. The problem with old code was relying on the length specified in a TRX header. That isn't reliable as TRX is commonly modified to have checksum cover only non-changing subpartitions. Otherwise modifying e.g. a rootfs would result in CRC32 mismatch and bootloader refusing to boot a firmware. Fix it by trying better to figure out a real TRX size. We can securely assume that TRX has to cover all subpartitions and the last one is at least of a block size in size. Then compare it with a length field. This makes code more optimal & reliable thanks to skipping data that shouldn't be parsed. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/bcm47xxpart.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index fe2581d9d882..1f0239848ebe 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -186,6 +186,8 @@ static int bcm47xxpart_parse(struct mtd_info *master, /* TRX */ if (buf[0x000 / 4] == TRX_MAGIC) { struct trx_header *trx; + uint32_t last_subpart; + uint32_t trx_size; if (trx_num >= ARRAY_SIZE(trx_parts)) pr_warn("No enough space to store another TRX found at 0x%X\n", @@ -195,11 +197,23 @@ static int bcm47xxpart_parse(struct mtd_info *master, bcm47xxpart_add_part(&parts[curr_part++], "firmware", offset, 0); - /* Jump to the end of TRX */ + /* + * Try to find TRX size. The "length" field isn't fully + * reliable as it could be decreased to make CRC32 cover + * only part of TRX data. It's commonly used as checksum + * can't cover e.g. ever-changing rootfs partition. + * Use offsets as helpers for assuming min TRX size. + */ trx = (struct trx_header *)buf; - offset = roundup(offset + trx->length, blocksize); - /* Next loop iteration will increase the offset */ - offset -= blocksize; + last_subpart = max3(trx->offset[0], trx->offset[1], + trx->offset[2]); + trx_size = max(trx->length, last_subpart + blocksize); + + /* + * Skip the TRX data. Decrease offset by block size as + * the next loop iteration will increase it. + */ + offset += roundup(trx_size, blocksize) - blocksize; continue; } -- cgit v1.2.3 From ea092fb3ce6668a357684bce993c782ceb0f167a Mon Sep 17 00:00:00 2001 From: Tokunori Ikegami Date: Tue, 8 May 2018 17:22:28 +0000 Subject: mtd: cfi_cmdset_0002: Fix coding style issues This patch fixes mainly to remove unneeded spaces after '(' and before ')'. Also some indentation errors are fixed. Signed-off-by: Tokunori Ikegami Cc: Chris Packham Cc: Brian Norris Cc: David Woodhouse Cc: Boris Brezillon Cc: Marek Vasut Cc: Richard Weinberger Cc: Cyrille Pitchen Cc: linux-mtd@lists.infradead.org Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 668e2cbc155b..bd0c30ad8ee7 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -44,8 +44,8 @@ #define MAX_WORD_RETRIES 3 -#define SST49LF004B 0x0060 -#define SST49LF040B 0x0050 +#define SST49LF004B 0x0060 +#define SST49LF040B 0x0050 #define SST49LF008A 0x005a #define AT49BV6416 0x00d6 @@ -207,7 +207,7 @@ static void fixup_use_write_buffers(struct mtd_info *mtd) struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; if (cfi->cfiq->BufWriteTimeoutTyp) { - pr_debug("Using buffer write method\n" ); + pr_debug("Using buffer write method\n"); mtd->_write = cfi_amdstd_write_buffers; } } @@ -1562,7 +1562,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, * depending of the conditions. The ' + 1' is to avoid having a * timeout of 0 jiffies if HZ is smaller than 1000. */ - unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; + unsigned long uWriteTimeout = (HZ / 1000) + 1; int ret = 0; map_word oldd; int retry_cnt = 0; @@ -1577,7 +1577,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, } pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", - __func__, adr, datum.x[0] ); + __func__, adr, datum.x[0]); if (mode == FL_OTP_WRITE) otp_enter(map, chip, adr, map_bankwidth(map)); @@ -1643,7 +1643,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, /* Did we succeed? */ if (!chip_good(map, adr, datum)) { /* reset on all failures. */ - map_write( map, CMD(0xF0), chip->start ); + map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ if (++retry_cnt <= MAX_WORD_RETRIES) @@ -1821,7 +1821,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, datum = map_word_load(map, buf); pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", - __func__, adr, datum.x[0] ); + __func__, adr, datum.x[0]); XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); @@ -2251,7 +2251,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) } pr_debug("MTD %s(): ERASE 0x%.8lx\n", - __func__, chip->start ); + __func__, chip->start); XIP_INVAL_CACHED_RANGE(map, adr, map->size); ENABLE_VPP(map); @@ -2297,7 +2297,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) if (time_after(jiffies, timeo)) { printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); + __func__); break; } @@ -2307,7 +2307,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) /* Did we succeed? */ if (!chip_good(map, adr, map_word_ff(map))) { /* reset on all failures. */ - map_write( map, CMD(0xF0), chip->start ); + map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ ret = -EIO; @@ -2340,7 +2340,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, } pr_debug("MTD %s(): ERASE 0x%.8lx\n", - __func__, adr ); + __func__, adr); XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); @@ -2389,7 +2389,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, if (time_after(jiffies, timeo)) { xip_enable(map, chip, adr); printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); + __func__); break; } @@ -2399,7 +2399,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, /* Did we succeed? */ if (!chip_good(map, adr, map_word_ff(map))) { /* reset on all failures. */ - map_write( map, CMD(0xF0), chip->start ); + map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ ret = -EIO; -- cgit v1.2.3 From 39138c1f4a31f3468fa2e44aba4495c0c30da205 Mon Sep 17 00:00:00 2001 From: "Wan, Jane (Nokia - US/Sunnyvale)" Date: Sun, 13 May 2018 04:30:02 +0000 Subject: mtd: rawnand: use bit-wise majority to recover the ONFI param page Per ONFI specification (Rev. 4.0), if all parameter pages have invalid CRC values, the bit-wise majority may be used to recover the contents of the parameter page from the parameter page copies present. Signed-off-by: Jane Wan Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 52 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 414a2a3a91d0..faf10c890a3d 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5086,6 +5086,37 @@ ext_out: return ret; } +/* + * Recover data with bit-wise majority + */ +static void nand_bit_wise_majority(const void **srcbufs, + unsigned int nsrcbufs, + void *dstbuf, + unsigned int bufsize) +{ + int i, j, k; + + for (i = 0; i < bufsize; i++) { + u8 val = 0; + + for (j = 0; j < 8; j++) { + unsigned int cnt = 0; + + for (k = 0; k < nsrcbufs; k++) { + const u8 *srcbuf = srcbufs[k]; + + if (srcbuf[i] & BIT(j)) + cnt++; + } + + if (cnt > nsrcbufs / 2) + val |= BIT(j); + } + + ((u8 *)dstbuf)[i] = val; + } +} + /* * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. */ @@ -5102,7 +5133,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) return 0; /* ONFI chip: allocate a buffer to hold its parameter page */ - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc((sizeof(*p) * 3), GFP_KERNEL); if (!p) return -ENOMEM; @@ -5113,21 +5144,32 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) } for (i = 0; i < 3; i++) { - ret = nand_read_data_op(chip, p, sizeof(*p), true); + ret = nand_read_data_op(chip, &p[i], sizeof(*p), true); if (ret) { ret = 0; goto free_onfi_param_page; } - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == + if (onfi_crc16(ONFI_CRC_BASE, (u8 *)&p[i], 254) == le16_to_cpu(p->crc)) { + if (i) + memcpy(p, &p[i], sizeof(*p)); break; } } if (i == 3) { - pr_err("Could not find valid ONFI parameter page; aborting\n"); - goto free_onfi_param_page; + const void *srcbufs[3] = {p, p + 1, p + 2}; + + pr_warn("Could not find a valid ONFI parameter page, trying bit-wise majority to recover it\n"); + nand_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p, + sizeof(*p)); + + if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) != + le16_to_cpu(p->crc)) { + pr_err("ONFI parameter recovery failed, aborting\n"); + goto free_onfi_param_page; + } } /* Check version */ -- cgit v1.2.3 From 782d1967d0479ffd59412b2f3179c8bb35f50ff6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 11 May 2018 14:44:07 +0200 Subject: mtd: rawnand: Do not check FAIL bit when executing a SET_FEATURES op The ONFI spec clearly says that FAIL bit is only valid for PROGRAM, ERASE and READ-with-on-die-ECC operations, and should be ignored otherwise. It seems that checking it after sending a SET_FEATURES is a bad idea because a previous READ, PROGRAM or ERASE op may have failed, and depending on the implementation, the FAIL bit is not cleared until a new READ, PROGRAM or ERASE is started. This leads to ->set_features() returning -EIO while it actually worked, which can sometimes stop a batch of READ/PROGRAM ops. Note that we only fix the ->exec_op() path here, because some drivers are abusing the NAND_STATUS_FAIL flag in their ->waitfunc() implementation to propagate other kind of errors, like wait-ready-timeout or controller-related errors. Let's not try to fix those drivers since they worked fine so far. Fixes: 8878b126df76 ("mtd: nand: add ->exec_op() implementation") Cc: stable@vger.kernel.org Signed-off-by: Boris Brezillon Acked-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index faf10c890a3d..327c60b06f87 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -2169,7 +2169,6 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, struct mtd_info *mtd = nand_to_mtd(chip); const u8 *params = data; int i, ret; - u8 status; if (chip->exec_op) { const struct nand_sdr_timings *sdr = @@ -2183,26 +2182,18 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, }; struct nand_operation op = NAND_OPERATION(instrs); - ret = nand_exec_op(chip, &op); - if (ret) - return ret; - - ret = nand_status_op(chip, &status); - if (ret) - return ret; - } else { - chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - chip->write_byte(mtd, params[i]); + return nand_exec_op(chip, &op); + } - ret = chip->waitfunc(mtd, chip); - if (ret < 0) - return ret; + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, params[i]); - status = ret; - } + ret = chip->waitfunc(mtd, chip); + if (ret < 0) + return ret; - if (status & NAND_STATUS_FAIL) + if (ret & NAND_STATUS_FAIL) return -EIO; return 0; -- cgit v1.2.3 From c7d6a82d90e193b1e4daba957e3908f26306d491 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 5 Feb 2018 14:32:59 +0300 Subject: mtd: spi-nor: intel-spi: Fix atomic sequence handling On many older systems using SW sequencer the PREOP_OPTYPE register contains two preopcodes as following: PREOP_OPTYPE=0xf2785006 The last two bytes are the opcodes decoded to: 0x50 - Write enable for volatile status register 0x06 - Write enable The former is used to modify volatile bits in the status register. For non-volatile bits the latter is needed. Preopcodes are used in SW sequencer to send one command "atomically" without anything else interfering the transfer. The sequence that gets executed is: - Send preopcode (write enable) from PREOP_OPTYPE register - Send the actual SPI command - Poll busy bit in the status register (0x05, RDSR) Commit 8c473dd61bb5 ("spi-nor: intel-spi: Don't assume OPMENU0/1 to be programmed by BIOS") enabled atomic sequence handling but because both preopcodes are programmed, the following happens: if (preop >> 8) val |= SSFSTS_CTL_SPOP; Since on these systems preop >> 8 == 0x50 we end up picking volatile write enable instead. Because of this the actual write command is pretty much NOP unless there is a WREN latched in the chip already. Furthermore we should not really just assume that WREN was issued in previous call to intel_spi_write_reg() because that might not be the case. This updates driver to first check that the opcode is actually available in PREOP_OPTYPE register and if not return error back to the spi-nor core (if the controller is not locked we program it now). In addition we save the opcode to ispi->atomic_preopcode field which is checked in next call to intel_spi_sw_cycle() to actually enable atomic sequence using the requested preopcode. Fixes: 8c473dd61bb5 ("spi-nor: intel-spi: Don't assume OPMENU0/1 to be programmed by BIOS") Signed-off-by: Mika Westerberg Cc: stable@vger.kernel.org Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/intel-spi.c | 76 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c index 699951523179..8e98f4ab87c1 100644 --- a/drivers/mtd/spi-nor/intel-spi.c +++ b/drivers/mtd/spi-nor/intel-spi.c @@ -136,6 +136,7 @@ * @swseq_reg: Use SW sequencer in register reads/writes * @swseq_erase: Use SW sequencer in erase operation * @erase_64k: 64k erase supported + * @atomic_preopcode: Holds preopcode when atomic sequence is requested * @opcodes: Opcodes which are supported. This are programmed by BIOS * before it locks down the controller. */ @@ -153,6 +154,7 @@ struct intel_spi { bool swseq_reg; bool swseq_erase; bool erase_64k; + u8 atomic_preopcode; u8 opcodes[8]; }; @@ -474,7 +476,7 @@ static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len, int optype) { u32 val = 0, status; - u16 preop; + u8 atomic_preopcode; int ret; ret = intel_spi_opcode_index(ispi, opcode, optype); @@ -484,17 +486,42 @@ static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len, if (len > INTEL_SPI_FIFO_SZ) return -EINVAL; + /* + * Always clear it after each SW sequencer operation regardless + * of whether it is successful or not. + */ + atomic_preopcode = ispi->atomic_preopcode; + ispi->atomic_preopcode = 0; + /* Only mark 'Data Cycle' bit when there is data to be transferred */ if (len > 0) val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS; val |= ret << SSFSTS_CTL_COP_SHIFT; val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE; val |= SSFSTS_CTL_SCGO; - preop = readw(ispi->sregs + PREOP_OPTYPE); - if (preop) { - val |= SSFSTS_CTL_ACS; - if (preop >> 8) - val |= SSFSTS_CTL_SPOP; + if (atomic_preopcode) { + u16 preop; + + switch (optype) { + case OPTYPE_WRITE_NO_ADDR: + case OPTYPE_WRITE_WITH_ADDR: + /* Pick matching preopcode for the atomic sequence */ + preop = readw(ispi->sregs + PREOP_OPTYPE); + if ((preop & 0xff) == atomic_preopcode) + ; /* Do nothing */ + else if ((preop >> 8) == atomic_preopcode) + val |= SSFSTS_CTL_SPOP; + else + return -EINVAL; + + /* Enable atomic sequence */ + val |= SSFSTS_CTL_ACS; + break; + + default: + return -EINVAL; + } + } writel(val, ispi->sregs + SSFSTS_CTL); @@ -538,13 +565,31 @@ static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) /* * This is handled with atomic operation and preop code in Intel - * controller so skip it here now. If the controller is not locked, - * program the opcode to the PREOP register for later use. + * controller so we only verify that it is available. If the + * controller is not locked, program the opcode to the PREOP + * register for later use. + * + * When hardware sequencer is used there is no need to program + * any opcodes (it handles them automatically as part of a command). */ if (opcode == SPINOR_OP_WREN) { - if (!ispi->locked) + u16 preop; + + if (!ispi->swseq_reg) + return 0; + + preop = readw(ispi->sregs + PREOP_OPTYPE); + if ((preop & 0xff) != opcode && (preop >> 8) != opcode) { + if (ispi->locked) + return -EINVAL; writel(opcode, ispi->sregs + PREOP_OPTYPE); + } + /* + * This enables atomic sequence on next SW sycle. Will + * be cleared after next operation. + */ + ispi->atomic_preopcode = opcode; return 0; } @@ -569,6 +614,13 @@ static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len, u32 val, status; ssize_t ret; + /* + * Atomic sequence is not expected with HW sequencer reads. Make + * sure it is cleared regardless. + */ + if (WARN_ON_ONCE(ispi->atomic_preopcode)) + ispi->atomic_preopcode = 0; + switch (nor->read_opcode) { case SPINOR_OP_READ: case SPINOR_OP_READ_FAST: @@ -627,6 +679,9 @@ static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len, u32 val, status; ssize_t ret; + /* Not needed with HW sequencer write, make sure it is cleared */ + ispi->atomic_preopcode = 0; + while (len > 0) { block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ); @@ -707,6 +762,9 @@ static int intel_spi_erase(struct spi_nor *nor, loff_t offs) return 0; } + /* Not needed with HW sequencer erase, make sure it is cleared */ + ispi->atomic_preopcode = 0; + while (len > 0) { writel(offs, ispi->base + FADDR); -- cgit v1.2.3 From 1f37033f05bf924b7f2d84a818fbba8dc2eac528 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 5 Feb 2018 14:33:00 +0300 Subject: mtd: spi-nor: intel-spi: Explicitly mark the driver as dangerous in Kconfig The driver is not meant for normal users at all but instead such users who really know what they are doing and are able to build their own kernel to enable it. Mark both driver Kconfig entries as dangerous to make sure the driver is not accidentally enabled without understanding possible consequences in doing so. Signed-off-by: Mika Westerberg Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 89da88e59121..f480b227a6b8 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -90,7 +90,7 @@ config SPI_INTEL_SPI tristate config SPI_INTEL_SPI_PCI - tristate "Intel PCH/PCU SPI flash PCI driver" + tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)" depends on X86 && PCI select SPI_INTEL_SPI help @@ -106,7 +106,7 @@ config SPI_INTEL_SPI_PCI will be called intel-spi-pci. config SPI_INTEL_SPI_PLATFORM - tristate "Intel PCH/PCU SPI flash platform driver" + tristate "Intel PCH/PCU SPI flash platform driver (DANGEROUS)" depends on X86 select SPI_INTEL_SPI help -- cgit v1.2.3 From dd50a1c4e56d6d2ea753f87a35b1f1e09cb877d7 Mon Sep 17 00:00:00 2001 From: Yogesh Gaur Date: Tue, 2 Jan 2018 16:09:12 +0530 Subject: mtd: spi-nor: fsl-quadspi: fix api naming typo _init_ahb_read Fix api naming typo _init_ahb_read fsl_qspi_init_abh_read --> fsl_qspi_init_ahb_read Signed-off-by: Yogesh Gaur Acked-by: Han Xu Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/fsl-quadspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 3e3c0bbc45c0..1b7f9aa694ea 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -661,7 +661,7 @@ static void fsl_qspi_set_map_addr(struct fsl_qspi *q) * causes the controller to clear the buffer, and use the sequence pointed * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash. */ -static void fsl_qspi_init_abh_read(struct fsl_qspi *q) +static void fsl_qspi_init_ahb_read(struct fsl_qspi *q) { void __iomem *base = q->iobase; int seqid; @@ -795,7 +795,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) fsl_qspi_init_lut(q); /* Init for AHB read */ - fsl_qspi_init_abh_read(q); + fsl_qspi_init_ahb_read(q); return 0; } -- cgit v1.2.3 From aba3a882a178c47b2dab1fd0bccca6131c01d9bb Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 13 Feb 2017 09:13:42 +0100 Subject: mtd: spi-nor: intel: provide a range for poll_timout The overall poll time here is INTEL_SPI_TIMEOUT * 1000 which is 5000 * 1000 - so 5seconds and it is coded as a tight loop here delay_us to readl_poll_timeout() is set to 0. As this is never called in an atomic context sleeping should be no issue and there is no reasons for the tight-loop here. Signed-off-by: Nicholas Mc Guire Acked-by: Mika Westerberg Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/intel-spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c index 8e98f4ab87c1..d2cbfc27826e 100644 --- a/drivers/mtd/spi-nor/intel-spi.c +++ b/drivers/mtd/spi-nor/intel-spi.c @@ -287,7 +287,7 @@ static int intel_spi_wait_hw_busy(struct intel_spi *ispi) u32 val; return readl_poll_timeout(ispi->base + HSFSTS_CTL, val, - !(val & HSFSTS_CTL_SCIP), 0, + !(val & HSFSTS_CTL_SCIP), 40, INTEL_SPI_TIMEOUT * 1000); } @@ -296,7 +296,7 @@ static int intel_spi_wait_sw_busy(struct intel_spi *ispi) u32 val; return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val, - !(val & SSFSTS_CTL_SCIP), 0, + !(val & SSFSTS_CTL_SCIP), 40, INTEL_SPI_TIMEOUT * 1000); } -- cgit v1.2.3 From 3278aa0ec39194d3aa719919c0f366867f1266eb Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 19 Jul 2017 17:25:47 +0200 Subject: mtd: spi-nor: stm32-quadspi: explicitly request exclusive reset control Commit a53e35db70d1 ("reset: Ensure drivers are explicit when requesting reset lines") started to transition the reset control request API calls to explicitly state whether the driver needs exclusive or shared reset control behavior. Convert all drivers requesting exclusive resets to the explicit API call so the temporary transition helpers can be removed. No functional changes. Cc: Cyrille Pitchen Cc: Marek Vasut Cc: David Woodhouse Cc: Brian Norris Cc: Boris Brezillon Cc: Richard Weinberger Cc: Maxime Coquelin Cc: Alexandre Torgue Cc: linux-mtd@lists.infradead.org Signed-off-by: Philipp Zabel Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/stm32-quadspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/stm32-quadspi.c b/drivers/mtd/spi-nor/stm32-quadspi.c index b3c7f6addba7..72553506a00b 100644 --- a/drivers/mtd/spi-nor/stm32-quadspi.c +++ b/drivers/mtd/spi-nor/stm32-quadspi.c @@ -656,7 +656,7 @@ static int stm32_qspi_probe(struct platform_device *pdev) return ret; } - rstc = devm_reset_control_get(dev, NULL); + rstc = devm_reset_control_get_exclusive(dev, NULL); if (!IS_ERR(rstc)) { reset_control_assert(rstc); udelay(2); -- cgit v1.2.3 From d728a7ea9037c2df085bf9494d56e90d0ff69d7d Mon Sep 17 00:00:00 2001 From: Yogesh Gaur Date: Tue, 30 Jan 2018 22:49:58 +0530 Subject: mtd: spi-nor: fsl-quadspi: add support for ls2080a/ls1080a LS2080a/LS1088a supports Freescale Quad SPI controller. Add fsl-quadspi driver support for ls2080a and ls1088a chip. Signed-off-by: Suresh Gupta Signed-off-by: Yogesh Gaur Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/fsl-quadspi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 1b7f9aa694ea..7d9620c7ff6c 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -214,6 +214,7 @@ enum fsl_qspi_devtype { FSL_QUADSPI_IMX7D, FSL_QUADSPI_IMX6UL, FSL_QUADSPI_LS1021A, + FSL_QUADSPI_LS2080A, }; struct fsl_qspi_devtype_data { @@ -267,6 +268,15 @@ static struct fsl_qspi_devtype_data ls1021a_data = { .driver_data = 0, }; +static const struct fsl_qspi_devtype_data ls2080a_data = { + .devtype = FSL_QUADSPI_LS2080A, + .rxfifo = 128, + .txfifo = 64, + .ahb_buf_size = 1024, + .driver_data = QUADSPI_QUIRK_TKT253890, +}; + + #define FSL_QSPI_MAX_CHIP 4 struct fsl_qspi { struct spi_nor nor[FSL_QSPI_MAX_CHIP]; @@ -806,6 +816,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = { { .compatible = "fsl,imx7d-qspi", .data = &imx7d_data, }, { .compatible = "fsl,imx6ul-qspi", .data = &imx6ul_data, }, { .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, }, + { .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); -- cgit v1.2.3 From 7fccf56ea4faef3f1e704acc18b8576b2eb9989d Mon Sep 17 00:00:00 2001 From: Stephen Douthit Date: Wed, 7 Mar 2018 18:55:57 -0500 Subject: mtd: spi-nor: Add Winbond w25q32jv support Datasheet: https://www.winbond.com/resource-files/w25q32jv%20dtr%20revf%2002242017.pdf Minimal testing done with fw_printenv/fw_setenv, test board did not support dual or quad access. Signed-off-by: Stephen Douthit Tested-by: Stephen Douthit Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 494b7a269872..3dda39b1cae9 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1215,6 +1215,11 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + "w25q32jv", INFO(0xef7016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { -- cgit v1.2.3 From d616f81cdd2a21edfa90a595a4e9b143f5ba8414 Mon Sep 17 00:00:00 2001 From: Kimmo Rautkoski Date: Mon, 14 May 2018 13:15:53 +0300 Subject: mtd: spi-nor: Add support for is25wp series chips Added support for is25wp032, is25wp064 and is25wp128. Signed-off-by: Kimmo Rautkoski Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 3dda39b1cae9..e6ead30d5949 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1065,6 +1065,12 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ) }, { "is25lp256", INFO(0x9d6019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ) }, + { "is25wp032", INFO(0x9d7016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25wp064", INFO(0x9d7017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, /* Macronix */ { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) }, -- cgit v1.2.3 From 771ff17e82fac54686f149e311d4ea74a70255b1 Mon Sep 17 00:00:00 2001 From: YuheiOKAWA Date: Thu, 17 May 2018 11:40:27 +0900 Subject: mtd: spi-nor: Add support for EN25QH32 Add support for Eon en25qh32 spi nor flash. Signed-off-by: YuheiOKAWA Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index e6ead30d5949..d9c368c44194 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -994,6 +994,7 @@ static const struct flash_info spi_nor_ids[] = { { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25qh32", INFO(0x1c7016, 0, 64 * 1024, 64, 0) }, { "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) }, { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) }, -- cgit v1.2.3 From cf589ce71e84d3b8811c65740645af254c5248c0 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 9 May 2018 10:17:29 +0200 Subject: mtd: bcm47xxpart: add of_match_table with a new DT binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows using bcm47xxpart parser to find partitions on flash described in DT using the "brcm,bcm947xx-cfe-partitions" compatible property. It means this parser doesn't have to be explicitly selected by a flash driver anymore. It can be used e.g. together with a generic m25p80 / spi-nor if device is just properly described. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/bcm47xxpart.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index 1f0239848ebe..0f93d2239352 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -304,9 +304,16 @@ static int bcm47xxpart_parse(struct mtd_info *master, return curr_part; }; +static const struct of_device_id bcm47xxpart_of_match_table[] = { + { .compatible = "brcm,bcm947xx-cfe-partitions" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm47xxpart_of_match_table); + static struct mtd_part_parser bcm47xxpart_mtd_parser = { .parse_fn = bcm47xxpart_parse, .name = "bcm47xxpart", + .of_match_table = bcm47xxpart_of_match_table, }; module_mtd_part_parser(bcm47xxpart_mtd_parser); -- cgit v1.2.3 From 34c819073c8b8b652ced18e53bd2b0332dbe29cf Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 May 2018 17:07:53 +0200 Subject: mtd: cmdlinepart: Update comment for introduction of OFFSET_CONTINUOUS The comment about offset zero was not updated when changing behavior: - Automatic offset calculation is indicated by OFFSET_CONTINUOUS, - Zero really means offset zero. Fixes: b175d03dd2072836 ("[PATCH] mtd cmdlinepart: allow zero offset value") Signed-off-by: Geert Uytterhoeven Signed-off-by: Boris Brezillon --- drivers/mtd/cmdlinepart.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index fbd5affc0acf..3ea44cff9b75 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -190,7 +190,10 @@ static struct mtd_partition * newpart(char *s, extra_mem = (unsigned char *)(parts + *num_parts); } - /* enter this partition (offset will be calculated later if it is zero at this point) */ + /* + * enter this partition (offset will be calculated later if it is + * OFFSET_CONTINUOUS at this point) + */ parts[this_part].size = size; parts[this_part].offset = offset; parts[this_part].mask_flags = mask_flags; -- cgit v1.2.3 From dfeae1073583dc35c33b32150e18b7048bbb37e6 Mon Sep 17 00:00:00 2001 From: Tokunori Ikegami Date: Wed, 30 May 2018 18:32:26 +0900 Subject: mtd: cfi_cmdset_0002: Change write buffer to check correct value For the word write it is checked if the chip has the correct value. But it is not checked for the write buffer as only checked if ready. To make sure for the write buffer change to check the value. It is enough as this patch is only checking the last written word. Since it is described by data sheets to check the operation status. Signed-off-by: Tokunori Ikegami Reviewed-by: Joakim Tjernlund Cc: Chris Packham Cc: Brian Norris Cc: David Woodhouse Cc: Boris Brezillon Cc: Marek Vasut Cc: Richard Weinberger Cc: Cyrille Pitchen Cc: linux-mtd@lists.infradead.org Cc: stable@vger.kernel.org Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index bd0c30ad8ee7..75d2c16029fd 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -1879,7 +1879,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, if (time_after(jiffies, timeo) && !chip_ready(map, adr)) break; - if (chip_ready(map, adr)) { + if (chip_good(map, adr, datum)) { xip_enable(map, chip, adr); goto op_done; } -- cgit v1.2.3 From 85a82e28b023de9b259a86824afbd6ba07bd6475 Mon Sep 17 00:00:00 2001 From: Tokunori Ikegami Date: Wed, 30 May 2018 18:32:27 +0900 Subject: mtd: cfi_cmdset_0002: Change definition naming to retry write operation The definition can be used for other program and erase operations also. So change the naming to MAX_RETRIES from MAX_WORD_RETRIES. Signed-off-by: Tokunori Ikegami Reviewed-by: Joakim Tjernlund Cc: Chris Packham Cc: Brian Norris Cc: David Woodhouse Cc: Boris Brezillon Cc: Marek Vasut Cc: Richard Weinberger Cc: Cyrille Pitchen Cc: linux-mtd@lists.infradead.org Cc: stable@vger.kernel.org Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 75d2c16029fd..62ada405fe93 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -42,7 +42,7 @@ #define AMD_BOOTLOC_BUG #define FORCE_WORD_WRITE 0 -#define MAX_WORD_RETRIES 3 +#define MAX_RETRIES 3 #define SST49LF004B 0x0060 #define SST49LF040B 0x0050 @@ -1646,7 +1646,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ - if (++retry_cnt <= MAX_WORD_RETRIES) + if (++retry_cnt <= MAX_RETRIES) goto retry; ret = -EIO; @@ -2105,7 +2105,7 @@ retry: map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ - if (++retry_cnt <= MAX_WORD_RETRIES) + if (++retry_cnt <= MAX_RETRIES) goto retry; ret = -EIO; -- cgit v1.2.3 From 45f75b8a919a4255f52df454f1ffdee0e42443b2 Mon Sep 17 00:00:00 2001 From: Tokunori Ikegami Date: Wed, 30 May 2018 18:32:28 +0900 Subject: mtd: cfi_cmdset_0002: Change erase functions to retry for error For the word write functions it is retried for error. But it is not implemented to retry for the erase functions. To make sure for the erase functions change to retry as same. This is needed to prevent the flash erase error caused only once. It was caused by the error case of chip_good() in the do_erase_oneblock(). Also it was confirmed on the MACRONIX flash device MX29GL512FHT2I-11G. But the error issue behavior is not able to reproduce at this moment. The flash controller is parallel Flash interface integrated on BCM53003. Signed-off-by: Tokunori Ikegami Reviewed-by: Joakim Tjernlund Cc: Chris Packham Cc: Brian Norris Cc: David Woodhouse Cc: Boris Brezillon Cc: Marek Vasut Cc: Richard Weinberger Cc: Cyrille Pitchen Cc: linux-mtd@lists.infradead.org Cc: stable@vger.kernel.org Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 62ada405fe93..217db661943d 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -2240,6 +2240,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) unsigned long int adr; DECLARE_WAITQUEUE(wait, current); int ret = 0; + int retry_cnt = 0; adr = cfi->addr_unlock1; @@ -2257,6 +2258,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) ENABLE_VPP(map); xip_disable(map, chip, adr); + retry: cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); @@ -2310,6 +2312,9 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ + if (++retry_cnt <= MAX_RETRIES) + goto retry; + ret = -EIO; } @@ -2329,6 +2334,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long timeo = jiffies + HZ; DECLARE_WAITQUEUE(wait, current); int ret = 0; + int retry_cnt = 0; adr += chip->start; @@ -2346,6 +2352,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, ENABLE_VPP(map); xip_disable(map, chip, adr); + retry: cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); @@ -2402,6 +2409,9 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ + if (++retry_cnt <= MAX_RETRIES) + goto retry; + ret = -EIO; } -- cgit v1.2.3 From 79ca484b613041ca223f74b34608bb6f5221724b Mon Sep 17 00:00:00 2001 From: Tokunori Ikegami Date: Wed, 30 May 2018 18:32:29 +0900 Subject: mtd: cfi_cmdset_0002: Change erase functions to check chip good only Currently the functions use to check both chip ready and good. But the chip ready is not enough to check the operation status. So change this to check the chip good instead of this. About the retry functions to make sure the error handling remain it. Signed-off-by: Tokunori Ikegami Reviewed-by: Joakim Tjernlund Cc: Chris Packham Cc: Brian Norris Cc: David Woodhouse Cc: Boris Brezillon Cc: Marek Vasut Cc: Richard Weinberger Cc: Cyrille Pitchen Cc: linux-mtd@lists.infradead.org Cc: stable@vger.kernel.org Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 217db661943d..d8e3a737c62f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -2294,12 +2294,13 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) chip->erase_suspended = 0; } - if (chip_ready(map, adr)) + if (chip_good(map, adr, map_word_ff(map))) break; if (time_after(jiffies, timeo)) { printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); + ret = -EIO; break; } @@ -2307,15 +2308,15 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) UDELAY(map, chip, adr, 1000000/HZ); } /* Did we succeed? */ - if (!chip_good(map, adr, map_word_ff(map))) { + if (ret) { /* reset on all failures. */ map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ - if (++retry_cnt <= MAX_RETRIES) + if (++retry_cnt <= MAX_RETRIES) { + ret = 0; goto retry; - - ret = -EIO; + } } chip->state = FL_READY; @@ -2388,7 +2389,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, chip->erase_suspended = 0; } - if (chip_ready(map, adr)) { + if (chip_good(map, adr, map_word_ff(map))) { xip_enable(map, chip, adr); break; } @@ -2397,6 +2398,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, xip_enable(map, chip, adr); printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); + ret = -EIO; break; } @@ -2404,15 +2406,15 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, UDELAY(map, chip, adr, 1000000/HZ); } /* Did we succeed? */ - if (!chip_good(map, adr, map_word_ff(map))) { + if (ret) { /* reset on all failures. */ map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ - if (++retry_cnt <= MAX_RETRIES) + if (++retry_cnt <= MAX_RETRIES) { + ret = 0; goto retry; - - ret = -EIO; + } } chip->state = FL_READY; -- cgit v1.2.3 From c64d4419a17cfb39a5b573f9016cd02ade4c9a64 Mon Sep 17 00:00:00 2001 From: Tokunori Ikegami Date: Wed, 30 May 2018 18:32:30 +0900 Subject: mtd: cfi_cmdset_0002: Change erase one block to enable XIP once To enable XIP it is executed both normal and error cases. This call can be moved after the for loop as same with erase chip. Signed-off-by: Tokunori Ikegami Reviewed-by: Joakim Tjernlund Cc: Chris Packham Cc: Brian Norris Cc: David Woodhouse Cc: Boris Brezillon Cc: Marek Vasut Cc: Richard Weinberger Cc: Cyrille Pitchen Cc: linux-mtd@lists.infradead.org Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index d8e3a737c62f..78b69ccd0b65 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -2389,13 +2389,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, chip->erase_suspended = 0; } - if (chip_good(map, adr, map_word_ff(map))) { - xip_enable(map, chip, adr); + if (chip_good(map, adr, map_word_ff(map))) break; - } if (time_after(jiffies, timeo)) { - xip_enable(map, chip, adr); printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); ret = -EIO; @@ -2418,6 +2415,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, } chip->state = FL_READY; + xip_enable(map, chip, adr); DISABLE_VPP(map); put_chip(map, chip, adr); mutex_unlock(&chip->mutex); -- cgit v1.2.3