From 392d39cfcaa9bcfa202fb0fd3bc65f3c20de318f Mon Sep 17 00:00:00 2001 From: Han Xu Date: Wed, 13 May 2015 14:40:57 -0500 Subject: mtd: fsl-quadspi: Access multiple chips simultaneously Add supports for simultaneous access to multiple chips. Need to lock the mutex before any quad spi operations and unlock the mutex after operations complete. Signed-off-by: Han Xu [Brian: reworked err path in fsl_qspi_prep()] Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 52a872fa1b6e..4fe13dd535f8 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -26,6 +26,7 @@ #include #include #include +#include /* The registers */ #define QUADSPI_MCR 0x00 @@ -233,6 +234,7 @@ struct fsl_qspi { u32 clk_rate; unsigned int chip_base_addr; /* We may support two chips. */ bool has_second_chip; + struct mutex lock; }; static inline int is_vybrid_qspi(struct fsl_qspi *q) @@ -761,18 +763,24 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) struct fsl_qspi *q = nor->priv; int ret; + mutex_lock(&q->lock); ret = clk_enable(q->clk_en); if (ret) - return ret; + goto err_mutex; ret = clk_enable(q->clk); - if (ret) { - clk_disable(q->clk_en); - return ret; - } + if (ret) + goto err_clk; fsl_qspi_set_base_addr(q, nor); return 0; + +err_clk: + clk_disable(q->clk_en); +err_mutex: + mutex_unlock(&q->lock); + + return ret; } static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) @@ -781,6 +789,7 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) clk_disable(q->clk); clk_disable(q->clk_en); + mutex_unlock(&q->lock); } static int fsl_qspi_probe(struct platform_device *pdev) @@ -864,6 +873,8 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (of_get_property(np, "fsl,qspi-has-second-chip", NULL)) q->has_second_chip = true; + mutex_init(&q->lock); + /* iterate the subnodes. */ for_each_available_child_of_node(dev->of_node, np) { char modalias[40]; @@ -892,24 +903,24 @@ static int fsl_qspi_probe(struct platform_device *pdev) ret = of_modalias_node(np, modalias, sizeof(modalias)); if (ret < 0) - goto irq_failed; + goto mutex_failed; ret = of_property_read_u32(np, "spi-max-frequency", &q->clk_rate); if (ret < 0) - goto irq_failed; + goto mutex_failed; /* set the chip address for READID */ fsl_qspi_set_base_addr(q, nor); ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD); if (ret) - goto irq_failed; + goto mutex_failed; ppdata.of_node = np; ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); if (ret) - goto irq_failed; + goto mutex_failed; /* Set the correct NOR size now. */ if (q->nor_size == 0) { @@ -950,6 +961,8 @@ last_init_failed: i *= 2; mtd_device_unregister(&q->mtd[i]); } +mutex_failed: + mutex_destroy(&q->lock); irq_failed: clk_disable_unprepare(q->clk); clk_failed: @@ -973,6 +986,7 @@ static int fsl_qspi_remove(struct platform_device *pdev) writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); writel(0x0, q->iobase + QUADSPI_RSER); + mutex_destroy(&q->lock); clk_unprepare(q->clk); clk_unprepare(q->clk_en); return 0; -- cgit v1.2.3 From ec7478fa173f65e5ee5fd2ba42c59ca3e700027b Mon Sep 17 00:00:00 2001 From: shengyong Date: Thu, 25 Jun 2015 02:23:13 +0000 Subject: mtd: nandsim: fix free of NULL pointer If allocating ns->nand_pages_slab fails, do not try to destroy it when cleaning up nandsim resources. Signed-off-by: Sheng Yong Signed-off-by: Brian Norris --- drivers/mtd/nand/nandsim.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 52c0c1a3899c..6a74f62a0033 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -649,7 +649,8 @@ static void free_device(struct nandsim *ns) kmem_cache_free(ns->nand_pages_slab, ns->pages[i].byte); } - kmem_cache_destroy(ns->nand_pages_slab); + if (ns->nand_pages_slab) + kmem_cache_destroy(ns->nand_pages_slab); vfree(ns->pages); } } -- cgit v1.2.3 From 5891a8d11f79b9a45a7334a63d0fa731875bc308 Mon Sep 17 00:00:00 2001 From: shengyong Date: Thu, 25 Jun 2015 02:23:14 +0000 Subject: mtd: nandsim: fix double free Do not call free_device() in init_nandsim, the caller - ns_init_module - will take care of that if something goes wrong. Signed-off-by: Sheng Yong Signed-off-by: Brian Norris --- drivers/mtd/nand/nandsim.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 6a74f62a0033..95d0cc49cfc2 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -730,8 +730,7 @@ static int init_nandsim(struct mtd_info *mtd) /* Fill the partition_info structure */ if (parts_num > ARRAY_SIZE(ns->partitions)) { NS_ERR("too many partitions.\n"); - ret = -EINVAL; - goto error; + return -EINVAL; } remains = ns->geom.totsz; next_offset = 0; @@ -740,14 +739,12 @@ static int init_nandsim(struct mtd_info *mtd) if (!part_sz || part_sz > remains) { NS_ERR("bad partition size.\n"); - ret = -EINVAL; - goto error; + return -EINVAL; } ns->partitions[i].name = get_partition_name(i); if (!ns->partitions[i].name) { NS_ERR("unable to allocate memory.\n"); - ret = -ENOMEM; - goto error; + return -ENOMEM; } ns->partitions[i].offset = next_offset; ns->partitions[i].size = part_sz; @@ -758,14 +755,12 @@ static int init_nandsim(struct mtd_info *mtd) if (remains) { if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) { NS_ERR("too many partitions.\n"); - ret = -EINVAL; - goto error; + return -EINVAL; } ns->partitions[i].name = get_partition_name(i); if (!ns->partitions[i].name) { NS_ERR("unable to allocate memory.\n"); - ret = -ENOMEM; - goto error; + return -ENOMEM; } ns->partitions[i].offset = next_offset; ns->partitions[i].size = remains; @@ -793,24 +788,18 @@ static int init_nandsim(struct mtd_info *mtd) printk("options: %#x\n", ns->options); if ((ret = alloc_device(ns)) != 0) - goto error; + return ret; /* Allocate / initialize the internal buffer */ ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); if (!ns->buf.byte) { NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", ns->geom.pgszoob); - ret = -ENOMEM; - goto error; + return -ENOMEM; } memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); return 0; - -error: - free_device(ns); - - return ret; } /* -- cgit v1.2.3 From a11244c0b25b79d2a3b07df429268d66736e5b45 Mon Sep 17 00:00:00 2001 From: Sandeep Paulraj Date: Tue, 19 Aug 2014 15:31:54 +0300 Subject: nand: davinci: add support for 4K page size nand devices It is needed for k2l keystone2 EVM which uses NAND flash with 4K page size, hence add support for 4K page size nand devices. [Brian: similar work submitted by Murali Karicheri and Hao Zhang ] Signed-off-by: Sandeep Paulraj Signed-off-by: Ivan Khoronzhuk Signed-off-by: Brian Norris --- drivers/mtd/nand/davinci_nand.c | 42 ++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index feb6d18de78d..b90801302df4 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -520,6 +520,32 @@ static struct nand_ecclayout hwecc4_2048 = { }, }; +/* + * An ECC layout for using 4-bit ECC with large-page (4096bytes) flash, + * storing ten ECC bytes plus the manufacturer's bad block marker byte, + * and not overlapping the default BBT markers. + */ +static struct nand_ecclayout hwecc4_4096 = { + .eccbytes = 80, + .eccpos = { + /* at the end of spare sector */ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + }, + .oobfree = { + /* 2 bytes at offset 0 hold manufacturer badblock markers */ + {.offset = 2, .length = 46, }, + /* 5 bytes at offset 8 hold BBT markers */ + /* 8 bytes at offset 16 hold JFFS2 clean markers */ + }, +}; + #if defined(CONFIG_OF) static const struct of_device_id davinci_nand_of_match[] = { {.compatible = "ti,davinci-nand", }, @@ -796,18 +822,12 @@ static int nand_davinci_probe(struct platform_device *pdev) info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; goto syndrome_done; } + if (chunks == 8) { + info->ecclayout = hwecc4_4096; + info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; + goto syndrome_done; + } - /* 4KiB page chips are not yet supported. The eccpos from - * nand_ecclayout cannot hold 80 bytes and change to eccpos[] - * breaks userspace ioctl interface with mtd-utils. Once we - * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used - * for the 4KiB page chips. - * - * TODO: Note that nand_ecclayout has now been expanded and can - * hold plenty of OOB entries. - */ - dev_warn(&pdev->dev, "no 4-bit ECC support yet " - "for 4KiB-page NAND\n"); ret = -EIO; goto err; -- cgit v1.2.3 From cef1ed9c6bcf69245c0b9eb89b3f3a45049ba10c Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Sun, 5 Jul 2015 09:57:41 +0800 Subject: mtd: r852: make ecc_reg 32-bit in r852_ecc_correct r852_ecc_correct() reads a 32-bit register into a 16-bit variable, ecc_reg, but this variable is later used as if it was larger. This is reported by clang when building the kernel with many warnings: drivers/mtd/nand/r852.c:512:11: error: shift count >= width of type [-Werror,-Wshift-count-overflow] ecc_reg >>= 16; ^ ~~ Fix this by making ecc_reg 32-bit, like the return type of r852_read_reg_dword(). Signed-off-by: Nicolas Iooss Signed-off-by: Brian Norris --- drivers/mtd/nand/r852.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 77e96d2df96c..cc6bac537f5a 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -466,7 +466,7 @@ static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) { - uint16_t ecc_reg; + uint32_t ecc_reg; uint8_t ecc_status, err_byte; int i, error = 0; -- cgit v1.2.3 From 038761dfe4ce145f0f080cc08ee43f6e0ab3ae2f Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Thu, 2 Jul 2015 11:37:56 +0200 Subject: mtd: fsl-quadspi: Actually clear TX FIFO upon write QUADSPI_MCR_CLR_TXF_MASK is the correct mask for clearing the TX FIFO. Signed-off-by: Alexander Stein Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 4fe13dd535f8..1946c6da76cd 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -539,7 +539,7 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, /* clear the TX FIFO. */ tmp = readl(q->iobase + QUADSPI_MCR); - writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR); + writel(tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR); /* fill the TX data to the FIFO */ for (j = 0, i = ((count + 3) / 4); j < i; j++) { -- cgit v1.2.3 From 0db7fae2736d0db920905c1fb576ec7eab2660c8 Mon Sep 17 00:00:00 2001 From: Alexey Firago Date: Tue, 30 Jun 2015 12:53:46 +0300 Subject: mtd: spi-nor: set SECT_4K for n25q064 SPI flash Micron n25q064 flash supports 4 KiB erase sectors. Signed-off-by: Alexey Firago Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index d78831b4422b..b2e8c3b72ea1 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -589,7 +589,7 @@ static const struct spi_device_id spi_nor_ids[] = { /* Micron */ { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, - { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SPI_NOR_QUAD_READ) }, + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) }, -- cgit v1.2.3 From 9c618292dba6aa9b82e8a2e4e6fe70f54bd87a81 Mon Sep 17 00:00:00 2001 From: Roy Spliet Date: Fri, 26 Jun 2015 11:00:10 +0200 Subject: mtd: nand: sunxi: Replace failsafe timing cfg with calculated value The TIMING_CFG register was previously statically set to a magic value (extracted from Allwinner's BSP) when initializing the NAND controller. Now that we have more details about the TIMING_CFG register layout (extracted from the A83 user manual) we can dynamically calculate the appropriate value for each NAND chip and set it when selecting the chip. Signed-off-by: Roy Spliet Acked-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/sunxi_nand.c | 73 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 6f93b2990d25..663e3314bb83 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -99,6 +99,12 @@ NFC_CMD_INT_ENABLE | \ NFC_DMA_INT_ENABLE) +/* define NFC_TIMING_CFG register layout */ +#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ + (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ + (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ + (((tCAD) & 0x7) << 8)) + /* define bit use in NFC_CMD */ #define NFC_CMD_LOW_BYTE GENMASK(7, 0) #define NFC_CMD_HIGH_BYTE GENMASK(15, 8) @@ -208,6 +214,7 @@ struct sunxi_nand_hw_ecc { * @nand: base NAND chip structure * @mtd: base MTD structure * @clk_rate: clk_rate required for this NAND chip + * @timing_cfg TIMING_CFG register value for this NAND chip * @selected: current active CS * @nsels: number of CS lines required by the NAND chip * @sels: array of CS lines descriptions @@ -217,6 +224,7 @@ struct sunxi_nand_chip { struct nand_chip nand; struct mtd_info mtd; unsigned long clk_rate; + u32 timing_cfg; int selected; int nsels; struct sunxi_nand_chip_sel sels[0]; @@ -403,6 +411,7 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) } } + writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); writel(ctl, nfc->regs + NFC_REG_CTL); sunxi_nand->selected = chip; @@ -807,10 +816,33 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, return 0; } +static const s32 tWB_lut[] = {6, 12, 16, 20}; +static const s32 tRHW_lut[] = {4, 8, 12, 20}; + +static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, + u32 clk_period) +{ + u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); + int i; + + for (i = 0; i < lut_size; i++) { + if (clk_cycles <= lut[i]) + return i; + } + + /* Doesn't fit */ + return -EINVAL; +} + +#define sunxi_nand_lookup_timing(l, p, c) \ + _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) + static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, const struct nand_sdr_timings *timings) { + struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller); u32 min_clk_period = 0; + s32 tWB, tADL, tWHR, tRHW, tCAD; /* T1 <=> tCLS */ if (timings->tCLS_min > min_clk_period) @@ -872,6 +904,41 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, if (timings->tWC_min > (min_clk_period * 2)) min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); + /* T16 - T19 + tCAD */ + tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, + min_clk_period); + if (tWB < 0) { + dev_err(nfc->dev, "unsupported tWB\n"); + return tWB; + } + + tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; + if (tADL > 3) { + dev_err(nfc->dev, "unsupported tADL\n"); + return -EINVAL; + } + + tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; + if (tWHR > 3) { + dev_err(nfc->dev, "unsupported tWHR\n"); + return -EINVAL; + } + + tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, + min_clk_period); + if (tRHW < 0) { + dev_err(nfc->dev, "unsupported tRHW\n"); + return tRHW; + } + + /* + * TODO: according to ONFI specs this value only applies for DDR NAND, + * but Allwinner seems to set this to 0x7. Mimic them for now. + */ + tCAD = 0x7; + + /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ + chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); /* Convert min_clk_period from picoseconds to nanoseconds */ min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); @@ -884,8 +951,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, */ chip->clk_rate = (2 * NSEC_PER_SEC) / min_clk_period; - /* TODO: configure T16-T19 */ - return 0; } @@ -1377,11 +1442,9 @@ static int sunxi_nfc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, nfc); /* - * TODO: replace these magic values with proper flags as soon as we - * know what they are encoding. + * TODO: replace this magic value with EDO flag */ writel(0x100, nfc->regs + NFC_REG_TIMING_CTL); - writel(0x7ff, nfc->regs + NFC_REG_TIMING_CFG); ret = sunxi_nand_chips_init(dev, nfc); if (ret) { -- cgit v1.2.3 From d052e508a4069f0711ea68156f4b1565bf14c41c Mon Sep 17 00:00:00 2001 From: Roy Spliet Date: Fri, 26 Jun 2015 11:00:11 +0200 Subject: mtd: nand: sunxi: Set serial access mode correctly Replaces the hard coded "always use EDO" policy with that prescribed by the ONFI 3.1 specification that EDO mode should always be used if tRC is below 30ns. Signed-off-by: Roy Spliet Acked-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/sunxi_nand.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 663e3314bb83..f97a58d6aae1 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -99,6 +99,9 @@ NFC_CMD_INT_ENABLE | \ NFC_DMA_INT_ENABLE) +/* define bit use in NFC_TIMING_CTL */ +#define NFC_TIMING_CTL_EDO BIT(8) + /* define NFC_TIMING_CFG register layout */ #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ @@ -225,6 +228,7 @@ struct sunxi_nand_chip { struct mtd_info mtd; unsigned long clk_rate; u32 timing_cfg; + u32 timing_ctl; int selected; int nsels; struct sunxi_nand_chip_sel sels[0]; @@ -411,6 +415,7 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) } } + writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); writel(ctl, nfc->regs + NFC_REG_CTL); @@ -940,6 +945,13 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); + /* + * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data + * output cycle timings shall be used if the host drives tRC less than + * 30 ns. + */ + chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0; + /* Convert min_clk_period from picoseconds to nanoseconds */ min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); @@ -1441,11 +1453,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, nfc); - /* - * TODO: replace this magic value with EDO flag - */ - writel(0x100, nfc->regs + NFC_REG_TIMING_CTL); - ret = sunxi_nand_chips_init(dev, nfc); if (ret) { dev_err(dev, "failed to init nand chips\n"); -- cgit v1.2.3 From 43163022927b6e7d202a7e6f939c3f392465494d Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 19 May 2015 14:38:22 -0700 Subject: mtd: m25p80: allow arbitrary OF matching for "jedec,spi-nor" When we added the "jedec,spi-nor" compatible string for use in this driver, we added it as a modalias option. The modalias can be derived in different ways for platform devices vs. device tree (of_*) matching. But for device tree matching (the primary target of this identifier string), the modalias is determined from the first entry in the 'compatible' property. IOW, the following properties would bind to this driver: // Option (a), modalias = "spi-nor" compatible = "jedec,spi-nor"; // Option (b), modalias = "spi-nor" compatible = "idontknowwhatimdoing,spi-nor"; But the following would not: // Option (c), modalias = "shinynewdevice" compatible = "myvendor,shinynewdevice", "jedec,spi-nor"; So, we'd like to match (a) and (c) (even when we don't have an explicit entry for "shinynewdevice"), and we'd rather not allow (b). To do this, we (1) always (for devices without specific platform data) pass the modalias to the spi-nor library; (2) rework the spi-nor library to not reject "bad" names, and instead just fall back to autodetection; and (3) add the .of_match_table to properly catch all "jedec,spi-nor". This allows (a) and (c) without warnings, and rejects (b). Signed-off-by: Brian Norris --- drivers/mtd/devices/m25p80.c | 18 +++++++++++------- drivers/mtd/spi-nor/spi-nor.c | 8 ++++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index d313f948b96c..9cd3631170ef 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -223,8 +223,6 @@ static int m25p_probe(struct spi_device *spi) */ if (data && data->type) flash_name = data->type; - else if (!strcmp(spi->modalias, "spi-nor")) - flash_name = NULL; /* auto-detect */ else flash_name = spi->modalias; @@ -289,19 +287,25 @@ static const struct spi_device_id m25p_ids[] = { {"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"}, {"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"}, - /* - * Generic support for SPI NOR that can be identified by the JEDEC READ - * ID opcode (0x9F). Use this, if possible. - */ - {"spi-nor"}, { }, }; MODULE_DEVICE_TABLE(spi, m25p_ids); +static const struct of_device_id m25p_of_table[] = { + /* + * Generic compatibility for SPI NOR that can be identified by the + * JEDEC READ ID opcode (0x9F). Use this, if possible. + */ + { .compatible = "jedec,spi-nor" }, + {} +}; +MODULE_DEVICE_TABLE(of, m25p_of_table); + static struct spi_driver m25p80_driver = { .driver = { .name = "m25p80", .owner = THIS_MODULE, + .of_match_table = m25p_of_table, }, .id_table = m25p_ids, .probe = m25p_probe, diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index b2e8c3b72ea1..47516d3af015 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1015,11 +1015,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) if (ret) return ret; - /* Try to auto-detect if chip name wasn't specified */ - if (!name) - id = spi_nor_read_id(nor); - else + if (name) id = spi_nor_match_id(name); + /* Try to auto-detect if chip name wasn't specified or not found */ + if (!id) + id = spi_nor_read_id(nor); if (IS_ERR_OR_NULL(id)) return -ENOENT; -- cgit v1.2.3 From 49bd706aac8fe7fa90988ecd3fd5c276575b194e Mon Sep 17 00:00:00 2001 From: Han Xu Date: Tue, 4 Aug 2015 10:25:22 -0500 Subject: mtd: spi-nor: fsl-quadspi: dynamically map memory space for AHB read QSPI may failed to map enough memory (256MB) for AHB read in previous implementation, especially in 3G/1G memory layout kernel. Dynamically map memory to avoid such issue. This implementation generally map QUADSPI_MAX_IOMAP (default 4MB) memory for AHB read, it should be enough for common scenarios, and the side effect (0.6% performance drop) is minor. Previous implementation root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K 32768+0 records in 32768+0 records out 33554432 bytes (34 MB) copied, 2.16006 s, 15.5 MB/s root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1 1+0 records in 1+0 records out 33554432 bytes (34 MB) copied, 1.43149 s, 23.4 MB/s After applied the patch root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K 32768+0 records in 32768+0 records out 33554432 bytes (34 MB) copied, 2.1743 s, 15.4 MB/s root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1 1+0 records in 1+0 records out 33554432 bytes (34 MB) copied, 1.43158 s, 23.4 MB/s Signed-off-by: Han Xu Signed-off-by: Frank Li Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 55 ++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 1946c6da76cd..2d460368ce5f 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -192,6 +192,8 @@ #define SEQID_EN4B 10 #define SEQID_BRWR 11 +#define QUADSPI_MIN_IOMAP SZ_4M + enum fsl_qspi_devtype { FSL_QUADSPI_VYBRID, FSL_QUADSPI_IMX6SX, @@ -223,8 +225,10 @@ struct fsl_qspi { struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; struct spi_nor nor[FSL_QSPI_MAX_CHIP]; void __iomem *iobase; - void __iomem *ahb_base; /* Used when read from AHB bus */ + void __iomem *ahb_addr; u32 memmap_phy; + u32 memmap_offs; + u32 memmap_len; struct clk *clk, *clk_en; struct device *dev; struct completion c; @@ -732,11 +736,42 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from, struct fsl_qspi *q = nor->priv; u8 cmd = nor->read_opcode; - dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n", - cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len); + /* if necessary,ioremap buffer before AHB read, */ + if (!q->ahb_addr) { + q->memmap_offs = q->chip_base_addr + from; + q->memmap_len = len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP; + + q->ahb_addr = ioremap_nocache( + q->memmap_phy + q->memmap_offs, + q->memmap_len); + if (!q->ahb_addr) { + dev_err(q->dev, "ioremap failed\n"); + return -ENOMEM; + } + /* ioremap if the data requested is out of range */ + } else if (q->chip_base_addr + from < q->memmap_offs + || q->chip_base_addr + from + len > + q->memmap_offs + q->memmap_len) { + iounmap(q->ahb_addr); + + q->memmap_offs = q->chip_base_addr + from; + q->memmap_len = len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP; + q->ahb_addr = ioremap_nocache( + q->memmap_phy + q->memmap_offs, + q->memmap_len); + if (!q->ahb_addr) { + dev_err(q->dev, "ioremap failed\n"); + return -ENOMEM; + } + } + + dev_dbg(q->dev, "cmd [%x],read from 0x%p, len:%d\n", + cmd, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, + len); /* Read out the data directly from the AHB buffer.*/ - memcpy(buf, q->ahb_base + q->chip_base_addr + from, len); + memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, + len); *retlen += len; return 0; @@ -821,9 +856,11 @@ static int fsl_qspi_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI-memory"); - q->ahb_base = devm_ioremap_resource(dev, res); - if (IS_ERR(q->ahb_base)) - return PTR_ERR(q->ahb_base); + if (!devm_request_mem_region(dev, res->start, resource_size(res), + res->name)) { + dev_err(dev, "can't request region for resource %pR\n", res); + return -EBUSY; + } q->memmap_phy = res->start; @@ -989,6 +1026,10 @@ static int fsl_qspi_remove(struct platform_device *pdev) mutex_destroy(&q->lock); clk_unprepare(q->clk); clk_unprepare(q->clk_en); + + if (q->ahb_addr) + iounmap(q->ahb_addr); + return 0; } -- cgit v1.2.3 From 80d37724089bc5d09fa12601a763bde01899ba88 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Tue, 4 Aug 2015 10:25:29 -0500 Subject: mtd: spi-nor: fsl-quadspi: use quirk to distinguish different qspi version add several quirk to distinguish different version of qspi module. Signed-off-by: Han Xu Signed-off-by: Frank Li Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 2d460368ce5f..91bfeac79722 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -28,6 +28,11 @@ #include #include +/* Controller needs driver to swap endian */ +#define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0) +/* Controller needs 4x internal clock */ +#define QUADSPI_QUIRK_4X_INT_CLK (1 << 1) + /* The registers */ #define QUADSPI_MCR 0x00 #define QUADSPI_MCR_RESERVED_SHIFT 16 @@ -204,20 +209,23 @@ struct fsl_qspi_devtype_data { int rxfifo; int txfifo; int ahb_buf_size; + int driver_data; }; static struct fsl_qspi_devtype_data vybrid_data = { .devtype = FSL_QUADSPI_VYBRID, .rxfifo = 128, .txfifo = 64, - .ahb_buf_size = 1024 + .ahb_buf_size = 1024, + .driver_data = QUADSPI_QUIRK_SWAP_ENDIAN, }; static struct fsl_qspi_devtype_data imx6sx_data = { .devtype = FSL_QUADSPI_IMX6SX, .rxfifo = 128, .txfifo = 512, - .ahb_buf_size = 1024 + .ahb_buf_size = 1024, + .driver_data = QUADSPI_QUIRK_4X_INT_CLK, }; #define FSL_QSPI_MAX_CHIP 4 @@ -241,14 +249,14 @@ struct fsl_qspi { struct mutex lock; }; -static inline int is_vybrid_qspi(struct fsl_qspi *q) +static inline int needs_swap_endian(struct fsl_qspi *q) { - return q->devtype_data->devtype == FSL_QUADSPI_VYBRID; + return q->devtype_data->driver_data & QUADSPI_QUIRK_SWAP_ENDIAN; } -static inline int is_imx6sx_qspi(struct fsl_qspi *q) +static inline int needs_4x_clock(struct fsl_qspi *q) { - return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX; + return q->devtype_data->driver_data & QUADSPI_QUIRK_4X_INT_CLK; } /* @@ -257,7 +265,7 @@ static inline int is_imx6sx_qspi(struct fsl_qspi *q) */ static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a) { - return is_vybrid_qspi(q) ? __swab32(a) : a; + return needs_swap_endian(q) ? __swab32(a) : a; } static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q) @@ -652,7 +660,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) unsigned long rate = q->clk_rate; int ret; - if (is_imx6sx_qspi(q)) + if (needs_4x_clock(q)) rate *= 4; ret = clk_set_rate(q->clk, rate); -- cgit v1.2.3 From d371cbfc151c6a7229150da0954d1f74ea4dcd39 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 4 Aug 2015 10:25:35 -0500 Subject: mtd: spi-nor: fsl-quadspi: add imx7d support Support i.mx7d. quadspi in i.mx7d increase rxfifo. require fill at least 16byte to trigger data transfer. Signed-off-by: Frank Li Signed-off-by: Han Xu Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 91bfeac79722..b567756b4cc2 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -32,6 +32,11 @@ #define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0) /* Controller needs 4x internal clock */ #define QUADSPI_QUIRK_4X_INT_CLK (1 << 1) +/* + * TKT253890, Controller needs driver to fill txfifo till 16 byte to + * trigger data transfer even though extern data will not transferred. + */ +#define QUADSPI_QUIRK_TKT253890 (1 << 2) /* The registers */ #define QUADSPI_MCR 0x00 @@ -202,6 +207,7 @@ enum fsl_qspi_devtype { FSL_QUADSPI_VYBRID, FSL_QUADSPI_IMX6SX, + FSL_QUADSPI_IMX7D, }; struct fsl_qspi_devtype_data { @@ -228,6 +234,15 @@ static struct fsl_qspi_devtype_data imx6sx_data = { .driver_data = QUADSPI_QUIRK_4X_INT_CLK, }; +static struct fsl_qspi_devtype_data imx7d_data = { + .devtype = FSL_QUADSPI_IMX7D, + .rxfifo = 512, + .txfifo = 512, + .ahb_buf_size = 1024, + .driver_data = QUADSPI_QUIRK_TKT253890 + | QUADSPI_QUIRK_4X_INT_CLK, +}; + #define FSL_QSPI_MAX_CHIP 4 struct fsl_qspi { struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; @@ -259,6 +274,11 @@ static inline int needs_4x_clock(struct fsl_qspi *q) return q->devtype_data->driver_data & QUADSPI_QUIRK_4X_INT_CLK; } +static inline int needs_fill_txfifo(struct fsl_qspi *q) +{ + return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT253890; +} + /* * An IC bug makes us to re-arrange the 32-bit data. * The following chips, such as IMX6SLX, have fixed this bug. @@ -560,6 +580,11 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, txbuf++; } + /* fill the TXFIFO upto 16 bytes for i.MX7d */ + if (needs_fill_txfifo(q)) + for (; i < 4; i++) + writel(tmp, q->iobase + QUADSPI_TBDR); + /* Trigger it */ ret = fsl_qspi_runcmd(q, opcode, to, count); @@ -679,6 +704,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) static const struct of_device_id fsl_qspi_dt_ids[] = { { .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, }, { .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, }, + { .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); -- cgit v1.2.3 From 151b49e19119da28dfcd29561147ec56913cbe61 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 4 Aug 2015 10:25:41 -0500 Subject: Documentation: fsl-quadspi: Add fsl, imx7d-qspi compatible string new compatible string: "fsl,imx7d-qspi" Signed-off-by: Frank Li Signed-off-by: Brian Norris --- Documentation/devicetree/bindings/mtd/fsl-quadspi.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt index 4461dc71cb10..ca72696d70ae 100644 --- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt +++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt @@ -1,7 +1,8 @@ * Freescale Quad Serial Peripheral Interface(QuadSPI) Required properties: - - compatible : Should be "fsl,vf610-qspi" or "fsl,imx6sx-qspi" + - compatible : Should be "fsl,vf610-qspi", "fsl,imx6sx-qspi", + "fsl,imx7d-qspi" - reg : the first contains the register location and length, the second contains the memory mapping address and length - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory" -- cgit v1.2.3 From 74a081d14f579d0a1de845ba63e8ee5f0c511258 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 4 Aug 2015 10:25:47 -0500 Subject: mtd: spi-nor: fsl-quadspi: add i.mx6ul support Add i.mx6ul chip support Signed-off-by: Frank Li Acked-by: Han xu Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index b567756b4cc2..a5db7ad8fc20 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -208,6 +208,7 @@ enum fsl_qspi_devtype { FSL_QUADSPI_VYBRID, FSL_QUADSPI_IMX6SX, FSL_QUADSPI_IMX7D, + FSL_QUADSPI_IMX6UL, }; struct fsl_qspi_devtype_data { @@ -243,6 +244,15 @@ static struct fsl_qspi_devtype_data imx7d_data = { | QUADSPI_QUIRK_4X_INT_CLK, }; +static struct fsl_qspi_devtype_data imx6ul_data = { + .devtype = FSL_QUADSPI_IMX6UL, + .rxfifo = 128, + .txfifo = 512, + .ahb_buf_size = 1024, + .driver_data = QUADSPI_QUIRK_TKT253890 + | QUADSPI_QUIRK_4X_INT_CLK, +}; + #define FSL_QSPI_MAX_CHIP 4 struct fsl_qspi { struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; @@ -705,6 +715,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = { { .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, }, { .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, }, { .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, }, + { .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); -- cgit v1.2.3 From cb94d0bb41e43b4fdd219ad930686397814bcd8d Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 4 Aug 2015 10:25:52 -0500 Subject: Documentation: fsl-quadspi: Add fsl, imx6ul-qspi compatible string new compatible string: "fsl,imx6ul-qspi". Signed-off-by: Frank Li Signed-off-by: Brian Norris --- Documentation/devicetree/bindings/mtd/fsl-quadspi.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt index ca72696d70ae..862aa2f8837a 100644 --- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt +++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt @@ -2,7 +2,7 @@ Required properties: - compatible : Should be "fsl,vf610-qspi", "fsl,imx6sx-qspi", - "fsl,imx7d-qspi" + "fsl,imx7d-qspi", "fsl,imx6ul-qspi" - reg : the first contains the register location and length, the second contains the memory mapping address and length - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory" -- cgit v1.2.3 From cacbef40aac478fb25fb7d18ac22d41a11ce06e2 Mon Sep 17 00:00:00 2001 From: Allen Xu Date: Tue, 4 Aug 2015 10:25:58 -0500 Subject: mtd: spi-nor: fsl-quadspi: i.MX6SX: fixed the random QSPI access failed issue We found there is a low probability(5%) QSPI access timeout issue, usually it happened on kernel boot stage, the first time kernel tried to access QSPI chip. The READ_ID command was sent but not executed, consequently the probe function failed. The root cause is that the divider is not glitchless in i.MX6SX chip. If qspi clock enabled then change clock frequency by call clk_set_rate, there will be glitch at low possiblity rate and pass to qspi controller. The controler will be hang by this glitch. Based on the new clock flag(CLK_SET_RATE_GATE) and new framework, we need to change the approach of seting clock rate. 1. Disable clock. 2. call clk_set_rate. 3. Enable clock again. Signed-off-by: Han Xu Signed-off-by: Frank Li Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 81 +++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index a5db7ad8fc20..8a69b2caa792 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -655,6 +655,32 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) q->iobase + QUADSPI_BFGENCR); } +/* This function was used to prepare and enable QSPI clock */ +static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q) +{ + int ret; + + ret = clk_prepare_enable(q->clk_en); + if (ret) + return ret; + + ret = clk_prepare_enable(q->clk); + if (ret) { + clk_disable_unprepare(q->clk_en); + return ret; + } + + return 0; +} + +/* This function was used to disable and unprepare QSPI clock */ +static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q) +{ + clk_disable_unprepare(q->clk); + clk_disable_unprepare(q->clk_en); + +} + /* We use this function to do some basic init for spi_nor_scan(). */ static int fsl_qspi_nor_setup(struct fsl_qspi *q) { @@ -662,11 +688,18 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q) u32 reg; int ret; - /* the default frequency, we will change it in the future.*/ + /* disable and unprepare clock to avoid glitch pass to controller */ + fsl_qspi_clk_disable_unprep(q); + + /* the default frequency, we will change it in the future. */ ret = clk_set_rate(q->clk, 66000000); if (ret) return ret; + ret = fsl_qspi_clk_prep_enable(q); + if (ret) + return ret; + /* Init the LUT table. */ fsl_qspi_init_lut(q); @@ -698,10 +731,17 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) if (needs_4x_clock(q)) rate *= 4; + /* disable and unprepare clock to avoid glitch pass to controller */ + fsl_qspi_clk_disable_unprep(q); + ret = clk_set_rate(q->clk, rate); if (ret) return ret; + ret = fsl_qspi_clk_prep_enable(q); + if (ret) + return ret; + /* Init the LUT table again. */ fsl_qspi_init_lut(q); @@ -844,22 +884,16 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) int ret; mutex_lock(&q->lock); - ret = clk_enable(q->clk_en); - if (ret) - goto err_mutex; - ret = clk_enable(q->clk); + ret = fsl_qspi_clk_prep_enable(q); if (ret) - goto err_clk; + goto err_mutex; fsl_qspi_set_base_addr(q, nor); return 0; -err_clk: - clk_disable(q->clk_en); err_mutex: mutex_unlock(&q->lock); - return ret; } @@ -867,8 +901,7 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) { struct fsl_qspi *q = nor->priv; - clk_disable(q->clk); - clk_disable(q->clk_en); + fsl_qspi_clk_disable_unprep(q); mutex_unlock(&q->lock); } @@ -918,15 +951,9 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (IS_ERR(q->clk)) return PTR_ERR(q->clk); - ret = clk_prepare_enable(q->clk_en); + ret = fsl_qspi_clk_prep_enable(q); if (ret) { - dev_err(dev, "cannot enable the qspi_en clock: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(q->clk); - if (ret) { - dev_err(dev, "cannot enable the qspi clock: %d\n", ret); + dev_err(dev, "can not enable the clock\n"); goto clk_failed; } @@ -1032,8 +1059,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (ret) goto last_init_failed; - clk_disable(q->clk); - clk_disable(q->clk_en); + fsl_qspi_clk_disable_unprep(q); return 0; last_init_failed: @@ -1046,9 +1072,9 @@ last_init_failed: mutex_failed: mutex_destroy(&q->lock); irq_failed: - clk_disable_unprepare(q->clk); + fsl_qspi_clk_disable_unprep(q); clk_failed: - clk_disable_unprepare(q->clk_en); + dev_err(dev, "Freescale QuadSPI probe failed\n"); return ret; } @@ -1069,8 +1095,6 @@ static int fsl_qspi_remove(struct platform_device *pdev) writel(0x0, q->iobase + QUADSPI_RSER); mutex_destroy(&q->lock); - clk_unprepare(q->clk); - clk_unprepare(q->clk_en); if (q->ahb_addr) iounmap(q->ahb_addr); @@ -1085,12 +1109,19 @@ static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state) static int fsl_qspi_resume(struct platform_device *pdev) { + int ret; struct fsl_qspi *q = platform_get_drvdata(pdev); + ret = fsl_qspi_clk_prep_enable(q); + if (ret) + return ret; + fsl_qspi_nor_setup(q); fsl_qspi_set_map_addr(q); fsl_qspi_nor_setup_last(q); + fsl_qspi_clk_disable_unprep(q); + return 0; } -- cgit v1.2.3 From 5cc66cb7345d1bac71e94d7fd05dc86506f59dfe Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 4 Aug 2015 10:26:04 -0500 Subject: mtd: spi-nor: fsl-quadspi: workaround qspi can't wakeup from wait mode QSPI1 cannot wake up CCM from WAIT mode on SX ARD board, add pmqos to let PM NOT enter WAIT mode when accessing QSPI1, refer to TKT245618. Signed-off-by: Frank Li Signed-off-by: Han Xu Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 8a69b2caa792..676b36361076 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -27,6 +27,7 @@ #include #include #include +#include /* Controller needs driver to swap endian */ #define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0) @@ -37,6 +38,8 @@ * trigger data transfer even though extern data will not transferred. */ #define QUADSPI_QUIRK_TKT253890 (1 << 2) +/* Controller cannot wake up from wait mode, TKT245618 */ +#define QUADSPI_QUIRK_TKT245618 (1 << 3) /* The registers */ #define QUADSPI_MCR 0x00 @@ -232,7 +235,8 @@ static struct fsl_qspi_devtype_data imx6sx_data = { .rxfifo = 128, .txfifo = 512, .ahb_buf_size = 1024, - .driver_data = QUADSPI_QUIRK_4X_INT_CLK, + .driver_data = QUADSPI_QUIRK_4X_INT_CLK + | QUADSPI_QUIRK_TKT245618, }; static struct fsl_qspi_devtype_data imx7d_data = { @@ -272,6 +276,7 @@ struct fsl_qspi { unsigned int chip_base_addr; /* We may support two chips. */ bool has_second_chip; struct mutex lock; + struct pm_qos_request pm_qos_req; }; static inline int needs_swap_endian(struct fsl_qspi *q) @@ -289,6 +294,11 @@ static inline int needs_fill_txfifo(struct fsl_qspi *q) return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT253890; } +static inline int needs_wakeup_wait_mode(struct fsl_qspi *q) +{ + return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618; +} + /* * An IC bug makes us to re-arrange the 32-bit data. * The following chips, such as IMX6SLX, have fixed this bug. @@ -670,12 +680,18 @@ static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q) return ret; } + if (needs_wakeup_wait_mode(q)) + pm_qos_add_request(&q->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0); + return 0; } /* This function was used to disable and unprepare QSPI clock */ static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q) { + if (needs_wakeup_wait_mode(q)) + pm_qos_remove_request(&q->pm_qos_req); + clk_disable_unprepare(q->clk); clk_disable_unprepare(q->clk_en); @@ -926,6 +942,10 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP) return -ENODEV; + q->dev = dev; + q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data; + platform_set_drvdata(pdev, q); + /* find the resources */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); q->iobase = devm_ioremap_resource(dev, res); @@ -971,10 +991,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) goto irq_failed; } - q->dev = dev; - q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data; - platform_set_drvdata(pdev, q); - ret = fsl_qspi_nor_setup(q); if (ret) goto irq_failed; -- cgit v1.2.3 From 8b8319c8b7d0385659c2df6376955cb6a1d918b6 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 4 Aug 2015 10:26:10 -0500 Subject: mtd: spi-nor: fsl-quadspi: reset the module in the probe The uboot may run the QuadSpi controler with command: #sf probe So we should reset the module in the probe. This patch also clear the pending interrupts which arised by the uboot code. Signed-off-by: Huang Shijie Signed-off-by: Frank Li Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 676b36361076..0144821a3692 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -716,6 +716,11 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q) if (ret) return ret; + /* Reset the module */ + writel(QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK, + base + QUADSPI_MCR); + udelay(1); + /* Init the LUT table. */ fsl_qspi_init_lut(q); @@ -733,6 +738,9 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q) writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK, base + QUADSPI_MCR); + /* clear all interrupt status */ + writel(0xffffffff, q->iobase + QUADSPI_FR); + /* enable the interrupt */ writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); -- cgit v1.2.3 From 788a6cddda9ba83276e37567c17bb78406904074 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 4 Aug 2015 10:26:16 -0500 Subject: mtd: spi-nor: fsl-quadspi: fix unsupported cmd when run flash_erase Erase function will use cmd 0x20 (SPINOR_OP_BE_4K) if kenrel enable option CONFIG_MTD_SPI_NOR_USE_4K_SECTORS. This command is not in fsl-quadspi driver LUT. So driver continue report fsl-quadspi 21e0000.qspi: Unsupported cmd 0x20. This patch fix this issue. Signed-off-by: Frank Li Acked-by: Han Xu Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/fsl-quadspi.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 0144821a3692..d32b7e04ccca 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -397,14 +397,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Erase a sector */ lut_base = SEQID_SE * 4; - if (q->nor_size <= SZ_16M) { - cmd = SPINOR_OP_SE; - addrlen = ADDR24BIT; - } else { - /* use the 4-byte address */ - cmd = SPINOR_OP_SE; - addrlen = ADDR32BIT; - } + cmd = q->nor[0].erase_opcode; + addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT; writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), base + QUADSPI_LUT(lut_base)); @@ -473,6 +467,8 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) case SPINOR_OP_BRWR: return SEQID_BRWR; default: + if (cmd == q->nor[0].erase_opcode) + return SEQID_SE; dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); break; } -- cgit v1.2.3 From ddb2c42b677eb2883e532f0928e445fc205d0019 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 6 Aug 2015 12:29:37 +0800 Subject: mtd: brcmnand: Fix misuse of IS_ENABLED While IS_ENABLED() is perfectly fine for CONFIG_* symbols, it is not for other symbols such as __BIG_ENDIAN that is provided directly by the compiler. Switch to use CONFIG_CPU_BIG_ENDIAN instead of __BIG_ENDIAN. Fixes: 27c5b17cd1b1 ("mtd: nand: add NAND driver "library" for Broadcom STB NAND controller") Signed-off-by: Axel Lin Signed-off-by: Brian Norris --- drivers/mtd/nand/brcmnand/brcmnand.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/brcmnand/brcmnand.h index a20c73630b7b..169f99e38a26 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.h +++ b/drivers/mtd/nand/brcmnand/brcmnand.h @@ -50,7 +50,7 @@ static inline u32 brcmnand_readl(void __iomem *addr) * Other architectures (e.g., ARM) either do not support big endian, or * else leave I/O in little endian mode. */ - if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) return __raw_readl(addr); else return readl_relaxed(addr); @@ -59,7 +59,7 @@ static inline u32 brcmnand_readl(void __iomem *addr) static inline void brcmnand_writel(u32 val, void __iomem *addr) { /* See brcmnand_readl() comments */ - if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) __raw_writel(val, addr); else writel_relaxed(val, addr); -- cgit v1.2.3 From c16340973fcb6461474a9f811f7f3ff2f946b24c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 3 Aug 2015 11:31:26 -0300 Subject: nand: pxa3xx: Increase initial buffer size The initial buffer is used for the initial commands used to detect a flash device (STATUS, READID and PARAM). ONFI param page is 256 bytes, and there are three redundant copies to be read. JEDEC param page is 512 bytes, and there are also three redundant copies to be read. Hence this buffer should be at least 512 x 3. This commits rounds the buffer size to 2048. Signed-off-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/nand/pxa3xx_nand.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 1259cc558ce9..9d973cd5bcab 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -45,10 +45,13 @@ /* * Define a buffer size for the initial command that detects the flash device: - * STATUS, READID and PARAM. The largest of these is the PARAM command, - * needing 256 bytes. + * STATUS, READID and PARAM. + * ONFI param page is 256 bytes, and there are three redundant copies + * to be read. JEDEC param page is 512 bytes, and there are also three + * redundant copies to be read. + * Hence this buffer should be at least 512 x 3. Let's pick 2048. */ -#define INIT_BUFFER_SIZE 256 +#define INIT_BUFFER_SIZE 2048 /* registers and bit definitions */ #define NDCR (0x00) /* Control register */ @@ -899,14 +902,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, break; case NAND_CMD_PARAM: - info->buf_count = 256; + info->buf_count = INIT_BUFFER_SIZE; info->ndcb0 |= NDCB0_CMD_TYPE(0) | NDCB0_ADDR_CYC(1) | NDCB0_LEN_OVRD | command; info->ndcb1 = (column & 0xFF); - info->ndcb3 = 256; - info->data_size = 256; + info->ndcb3 = INIT_BUFFER_SIZE; + info->data_size = INIT_BUFFER_SIZE; break; case NAND_CMD_READID: -- cgit v1.2.3 From 89f271c4d052063a72af2622f285f70caac91845 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 9 Jul 2015 22:19:07 +0200 Subject: doc: dt: add documentation for nxp,lpc1773-spifi Add device tree binding documentation for the SPI Flash Interface (SPIFI) found on NXP LPC18xx and LPC43xx devies. Signed-off-by: Joachim Eastwood Signed-off-by: Brian Norris --- .../devicetree/bindings/mtd/nxp-spifi.txt | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/nxp-spifi.txt diff --git a/Documentation/devicetree/bindings/mtd/nxp-spifi.txt b/Documentation/devicetree/bindings/mtd/nxp-spifi.txt new file mode 100644 index 000000000000..f8b6b250654e --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/nxp-spifi.txt @@ -0,0 +1,58 @@ +* NXP SPI Flash Interface (SPIFI) + +NXP SPIFI is a specialized SPI interface for serial Flash devices. +It supports one Flash device with 1-, 2- and 4-bits width in SPI +mode 0 or 3. The controller operates in either command or memory +mode. In memory mode the Flash is accessible from the CPU as +normal memory. + +Required properties: + - compatible : Should be "nxp,lpc1773-spifi" + - reg : the first contains the register location and length, + the second contains the memory mapping address and length + - reg-names: Should contain the reg names "spifi" and "flash" + - interrupts : Should contain the interrupt for the device + - clocks : The clocks needed by the SPIFI controller + - clock-names : Should contain the clock names "spifi" and "reg" + +Optional properties: + - resets : phandle + reset specifier + +The SPI Flash must be a child of the SPIFI node and must have a +compatible property as specified in bindings/mtd/jedec,spi-nor.txt + +Optionally it can also contain the following properties. + - spi-cpol : Controller only supports mode 0 and 3 so either + both spi-cpol and spi-cpha should be present or + none of them + - spi-cpha : See above + - spi-rx-bus-width : Used to select how many pins that are used + for input on the controller + +See bindings/spi/spi-bus.txt for more information. + +Example: +spifi: spifi@40003000 { + compatible = "nxp,lpc1773-spifi"; + reg = <0x40003000 0x1000>, <0x14000000 0x4000000>; + reg-names = "spifi", "flash"; + interrupts = <30>; + clocks = <&ccu1 CLK_SPIFI>, <&ccu1 CLK_CPU_SPIFI>; + clock-names = "spifi", "reg"; + resets = <&rgu 53>; + + flash@0 { + compatible = "jedec,spi-nor"; + spi-cpol; + spi-cpha; + spi-rx-bus-width = <4>; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "data"; + reg = <0 0x200000>; + }; + }; +}; + -- cgit v1.2.3 From f617b9587c1662a2352b90cb64ad8bcf0ff4d0af Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 13 Aug 2015 19:19:40 +0200 Subject: mtd: spi-nor: add driver for NXP SPI Flash Interface (SPIFI) Add SPI-NOR driver for the SPI Flash Interface (SPIFI) controller that is found on newer NXP MCU devices. The controller supports serial SPI Flash devices with 1-, 2- and 4-bit width in either SPI mode 0 or 3. The controller can operate in either command or memory mode. In memory mode the Flash is exposed as normal memory and can be directly accessed by the CPU. Signed-off-by: Joachim Eastwood Reviewed-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/Kconfig | 11 + drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/nxp-spifi.c | 482 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 494 insertions(+) create mode 100644 drivers/mtd/spi-nor/nxp-spifi.c diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 64a4f0edabc7..407142e411fa 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -28,4 +28,15 @@ config SPI_FSL_QUADSPI This enables support for the Quad SPI controller in master mode. We only connect the NOR to this controller now. +config SPI_NXP_SPIFI + tristate "NXP SPI Flash Interface (SPIFI)" + depends on OF && (ARCH_LPC18XX || COMPILE_TEST) + depends on HAS_IOMEM + help + Enable support for the NXP LPC SPI Flash Interface controller. + + SPIFI is a specialized controller for connecting serial SPI + Flash. Enable this option if you have a device with a SPIFI + controller and want to access the Flash as a mtd device. + endif # MTD_SPI_NOR diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 6a7ce1462247..e53333ef8582 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o +obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c new file mode 100644 index 000000000000..9ad1dd0896c0 --- /dev/null +++ b/drivers/mtd/spi-nor/nxp-spifi.c @@ -0,0 +1,482 @@ +/* + * SPI-NOR driver for NXP SPI Flash Interface (SPIFI) + * + * Copyright (C) 2015 Joachim Eastwood + * + * Based on Freescale QuadSPI driver: + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* NXP SPIFI registers, bits and macros */ +#define SPIFI_CTRL 0x000 +#define SPIFI_CTRL_TIMEOUT(timeout) (timeout) +#define SPIFI_CTRL_CSHIGH(cshigh) ((cshigh) << 16) +#define SPIFI_CTRL_MODE3 BIT(23) +#define SPIFI_CTRL_DUAL BIT(28) +#define SPIFI_CTRL_FBCLK BIT(30) +#define SPIFI_CMD 0x004 +#define SPIFI_CMD_DATALEN(dlen) ((dlen) & 0x3fff) +#define SPIFI_CMD_DOUT BIT(15) +#define SPIFI_CMD_INTLEN(ilen) ((ilen) << 16) +#define SPIFI_CMD_FIELDFORM(field) ((field) << 19) +#define SPIFI_CMD_FIELDFORM_ALL_SERIAL SPIFI_CMD_FIELDFORM(0x0) +#define SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA SPIFI_CMD_FIELDFORM(0x1) +#define SPIFI_CMD_FRAMEFORM(frame) ((frame) << 21) +#define SPIFI_CMD_FRAMEFORM_OPCODE_ONLY SPIFI_CMD_FRAMEFORM(0x1) +#define SPIFI_CMD_OPCODE(op) ((op) << 24) +#define SPIFI_ADDR 0x008 +#define SPIFI_IDATA 0x00c +#define SPIFI_CLIMIT 0x010 +#define SPIFI_DATA 0x014 +#define SPIFI_MCMD 0x018 +#define SPIFI_STAT 0x01c +#define SPIFI_STAT_MCINIT BIT(0) +#define SPIFI_STAT_CMD BIT(1) +#define SPIFI_STAT_RESET BIT(4) + +#define SPI_NOR_MAX_ID_LEN 6 + +struct nxp_spifi { + struct device *dev; + struct clk *clk_spifi; + struct clk *clk_reg; + void __iomem *io_base; + void __iomem *flash_base; + struct mtd_info mtd; + struct spi_nor nor; + bool memory_mode; + u32 mcmd; +}; + +static int nxp_spifi_wait_for_cmd(struct nxp_spifi *spifi) +{ + u8 stat; + int ret; + + ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat, + !(stat & SPIFI_STAT_CMD), 10, 30); + if (ret) + dev_warn(spifi->dev, "command timed out\n"); + + return ret; +} + +static int nxp_spifi_reset(struct nxp_spifi *spifi) +{ + u8 stat; + int ret; + + writel(SPIFI_STAT_RESET, spifi->io_base + SPIFI_STAT); + ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat, + !(stat & SPIFI_STAT_RESET), 10, 30); + if (ret) + dev_warn(spifi->dev, "state reset timed out\n"); + + return ret; +} + +static int nxp_spifi_set_memory_mode_off(struct nxp_spifi *spifi) +{ + int ret; + + if (!spifi->memory_mode) + return 0; + + ret = nxp_spifi_reset(spifi); + if (ret) + dev_err(spifi->dev, "unable to enter command mode\n"); + else + spifi->memory_mode = false; + + return ret; +} + +static int nxp_spifi_set_memory_mode_on(struct nxp_spifi *spifi) +{ + u8 stat; + int ret; + + if (spifi->memory_mode) + return 0; + + writel(spifi->mcmd, spifi->io_base + SPIFI_MCMD); + ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat, + stat & SPIFI_STAT_MCINIT, 10, 30); + if (ret) + dev_err(spifi->dev, "unable to enter memory mode\n"); + else + spifi->memory_mode = true; + + return ret; +} + +static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + struct nxp_spifi *spifi = nor->priv; + u32 cmd; + int ret; + + ret = nxp_spifi_set_memory_mode_off(spifi); + if (ret) + return ret; + + cmd = SPIFI_CMD_DATALEN(len) | + SPIFI_CMD_OPCODE(opcode) | + SPIFI_CMD_FIELDFORM_ALL_SERIAL | + SPIFI_CMD_FRAMEFORM_OPCODE_ONLY; + writel(cmd, spifi->io_base + SPIFI_CMD); + + while (len--) + *buf++ = readb(spifi->io_base + SPIFI_DATA); + + return nxp_spifi_wait_for_cmd(spifi); +} + +static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + int len, int write_enable) +{ + struct nxp_spifi *spifi = nor->priv; + u32 cmd; + int ret; + + ret = nxp_spifi_set_memory_mode_off(spifi); + if (ret) + return ret; + + cmd = SPIFI_CMD_DOUT | + SPIFI_CMD_DATALEN(len) | + SPIFI_CMD_OPCODE(opcode) | + SPIFI_CMD_FIELDFORM_ALL_SERIAL | + SPIFI_CMD_FRAMEFORM_OPCODE_ONLY; + writel(cmd, spifi->io_base + SPIFI_CMD); + + while (len--) + writeb(*buf++, spifi->io_base + SPIFI_DATA); + + return nxp_spifi_wait_for_cmd(spifi); +} + +static int nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct nxp_spifi *spifi = nor->priv; + int ret; + + ret = nxp_spifi_set_memory_mode_on(spifi); + if (ret) + return ret; + + memcpy_fromio(buf, spifi->flash_base + from, len); + *retlen += len; + + return 0; +} + +static void nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct nxp_spifi *spifi = nor->priv; + u32 cmd; + int ret; + + ret = nxp_spifi_set_memory_mode_off(spifi); + if (ret) + return; + + writel(to, spifi->io_base + SPIFI_ADDR); + *retlen += len; + + cmd = SPIFI_CMD_DOUT | + SPIFI_CMD_DATALEN(len) | + SPIFI_CMD_FIELDFORM_ALL_SERIAL | + SPIFI_CMD_OPCODE(nor->program_opcode) | + SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); + writel(cmd, spifi->io_base + SPIFI_CMD); + + while (len--) + writeb(*buf++, spifi->io_base + SPIFI_DATA); + + nxp_spifi_wait_for_cmd(spifi); +} + +static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs) +{ + struct nxp_spifi *spifi = nor->priv; + u32 cmd; + int ret; + + ret = nxp_spifi_set_memory_mode_off(spifi); + if (ret) + return ret; + + writel(offs, spifi->io_base + SPIFI_ADDR); + + cmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL | + SPIFI_CMD_OPCODE(nor->erase_opcode) | + SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); + writel(cmd, spifi->io_base + SPIFI_CMD); + + return nxp_spifi_wait_for_cmd(spifi); +} + +static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi) +{ + switch (spifi->nor.flash_read) { + case SPI_NOR_NORMAL: + case SPI_NOR_FAST: + spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL; + break; + case SPI_NOR_DUAL: + case SPI_NOR_QUAD: + spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA; + break; + default: + dev_err(spifi->dev, "unsupported SPI read mode\n"); + return -EINVAL; + } + + /* Memory mode supports address length between 1 and 4 */ + if (spifi->nor.addr_width < 1 || spifi->nor.addr_width > 4) + return -EINVAL; + + spifi->mcmd |= SPIFI_CMD_OPCODE(spifi->nor.read_opcode) | + SPIFI_CMD_INTLEN(spifi->nor.read_dummy / 8) | + SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); + + return 0; +} + +static void nxp_spifi_dummy_id_read(struct spi_nor *nor) +{ + u8 id[SPI_NOR_MAX_ID_LEN]; + nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); +} + +static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, + struct device_node *np) +{ + struct mtd_part_parser_data ppdata; + enum read_mode flash_read; + u32 ctrl, property; + u16 mode = 0; + int ret; + + if (!of_property_read_u32(np, "spi-rx-bus-width", &property)) { + switch (property) { + case 1: + break; + case 2: + mode |= SPI_RX_DUAL; + break; + case 4: + mode |= SPI_RX_QUAD; + break; + default: + dev_err(spifi->dev, "unsupported rx-bus-width\n"); + return -EINVAL; + } + } + + if (of_find_property(np, "spi-cpha", NULL)) + mode |= SPI_CPHA; + + if (of_find_property(np, "spi-cpol", NULL)) + mode |= SPI_CPOL; + + /* Setup control register defaults */ + ctrl = SPIFI_CTRL_TIMEOUT(1000) | + SPIFI_CTRL_CSHIGH(15) | + SPIFI_CTRL_FBCLK; + + if (mode & SPI_RX_DUAL) { + ctrl |= SPIFI_CTRL_DUAL; + flash_read = SPI_NOR_DUAL; + } else if (mode & SPI_RX_QUAD) { + ctrl &= ~SPIFI_CTRL_DUAL; + flash_read = SPI_NOR_QUAD; + } else { + ctrl |= SPIFI_CTRL_DUAL; + flash_read = SPI_NOR_NORMAL; + } + + switch (mode & (SPI_CPHA | SPI_CPOL)) { + case SPI_MODE_0: + ctrl &= ~SPIFI_CTRL_MODE3; + break; + case SPI_MODE_3: + ctrl |= SPIFI_CTRL_MODE3; + break; + default: + dev_err(spifi->dev, "only mode 0 and 3 supported\n"); + return -EINVAL; + } + + writel(ctrl, spifi->io_base + SPIFI_CTRL); + + spifi->mtd.priv = &spifi->nor; + spifi->nor.mtd = &spifi->mtd; + spifi->nor.dev = spifi->dev; + spifi->nor.priv = spifi; + spifi->nor.read = nxp_spifi_read; + spifi->nor.write = nxp_spifi_write; + spifi->nor.erase = nxp_spifi_erase; + spifi->nor.read_reg = nxp_spifi_read_reg; + spifi->nor.write_reg = nxp_spifi_write_reg; + + /* + * The first read on a hard reset isn't reliable so do a + * dummy read of the id before calling spi_nor_scan(). + * The reason for this problem is unknown. + * + * The official NXP spifilib uses more or less the same + * workaround that is applied here by reading the device + * id multiple times. + */ + nxp_spifi_dummy_id_read(&spifi->nor); + + ret = spi_nor_scan(&spifi->nor, NULL, flash_read); + if (ret) { + dev_err(spifi->dev, "device scan failed\n"); + return ret; + } + + ret = nxp_spifi_setup_memory_cmd(spifi); + if (ret) { + dev_err(spifi->dev, "memory command setup failed\n"); + return ret; + } + + ppdata.of_node = np; + ret = mtd_device_parse_register(&spifi->mtd, NULL, &ppdata, NULL, 0); + if (ret) { + dev_err(spifi->dev, "mtd device parse failed\n"); + return ret; + } + + return 0; +} + +static int nxp_spifi_probe(struct platform_device *pdev) +{ + struct device_node *flash_np; + struct nxp_spifi *spifi; + struct resource *res; + int ret; + + spifi = devm_kzalloc(&pdev->dev, sizeof(*spifi), GFP_KERNEL); + if (!spifi) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spifi"); + spifi->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spifi->io_base)) + return PTR_ERR(spifi->io_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash"); + spifi->flash_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spifi->flash_base)) + return PTR_ERR(spifi->flash_base); + + spifi->clk_spifi = devm_clk_get(&pdev->dev, "spifi"); + if (IS_ERR(spifi->clk_spifi)) { + dev_err(&pdev->dev, "spifi clock not found\n"); + return PTR_ERR(spifi->clk_spifi); + } + + spifi->clk_reg = devm_clk_get(&pdev->dev, "reg"); + if (IS_ERR(spifi->clk_reg)) { + dev_err(&pdev->dev, "reg clock not found\n"); + return PTR_ERR(spifi->clk_reg); + } + + ret = clk_prepare_enable(spifi->clk_reg); + if (ret) { + dev_err(&pdev->dev, "unable to enable reg clock\n"); + return ret; + } + + ret = clk_prepare_enable(spifi->clk_spifi); + if (ret) { + dev_err(&pdev->dev, "unable to enable spifi clock\n"); + goto dis_clk_reg; + } + + spifi->dev = &pdev->dev; + platform_set_drvdata(pdev, spifi); + + /* Initialize and reset device */ + nxp_spifi_reset(spifi); + writel(0, spifi->io_base + SPIFI_IDATA); + writel(0, spifi->io_base + SPIFI_MCMD); + nxp_spifi_reset(spifi); + + flash_np = of_get_next_available_child(pdev->dev.of_node, NULL); + if (!flash_np) { + dev_err(&pdev->dev, "no SPI flash device to configure\n"); + ret = -ENODEV; + goto dis_clks; + } + + ret = nxp_spifi_setup_flash(spifi, flash_np); + if (ret) { + dev_err(&pdev->dev, "unable to setup flash chip\n"); + goto dis_clks; + } + + return 0; + +dis_clks: + clk_disable_unprepare(spifi->clk_spifi); +dis_clk_reg: + clk_disable_unprepare(spifi->clk_reg); + return ret; +} + +static int nxp_spifi_remove(struct platform_device *pdev) +{ + struct nxp_spifi *spifi = platform_get_drvdata(pdev); + + mtd_device_unregister(&spifi->mtd); + clk_disable_unprepare(spifi->clk_spifi); + clk_disable_unprepare(spifi->clk_reg); + + return 0; +} + +static const struct of_device_id nxp_spifi_match[] = { + {.compatible = "nxp,lpc1773-spifi"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, nxp_spifi_match); + +static struct platform_driver nxp_spifi_driver = { + .probe = nxp_spifi_probe, + .remove = nxp_spifi_remove, + .driver = { + .name = "nxp-spifi", + .of_match_table = nxp_spifi_match, + }, +}; +module_platform_driver(nxp_spifi_driver); + +MODULE_DESCRIPTION("NXP SPI Flash Interface driver"); +MODULE_AUTHOR("Joachim Eastwood "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 842c12ddb49ef271ff978cbd022777d3d2c7787f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 13 Aug 2015 14:02:05 -0300 Subject: mtd: spi-nor: Improve Kconfig help text for SPI_FSL_QUADSPI The current "We only connect the NOR to this controller now." text is not very clear, so explain it better by saying that generic SPI is not supported by SPI_FSL_QUADSPI and only SPI NOR is. Signed-off-by: Fabio Estevam Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 407142e411fa..89bf4c1faa2b 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -26,7 +26,8 @@ config SPI_FSL_QUADSPI depends on ARCH_MXC help This enables support for the Quad SPI controller in master mode. - We only connect the NOR to this controller now. + This controller does not support generic SPI. It only supports + SPI NOR. config SPI_NXP_SPIFI tristate "NXP SPI Flash Interface (SPIFI)" -- cgit v1.2.3 From b4d97f022ac07c26d8f41f105b6c9c9a699875a2 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Tue, 16 Jun 2015 11:37:04 +0300 Subject: mtd: spi-nor: add Spansion S25FL204K support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spansion S25FL204K is a 4-Mbit 3.0V Serial Flash Memory with Uniform 4 kB Sectors. Signed-off-by: Antony Pavlov Acked-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 47516d3af015..47df4b5eae2f 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -626,6 +626,7 @@ static const struct spi_device_id spi_nor_ids[] = { { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) }, { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) }, + { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K) }, /* SST -- large erase sizes are "overlays", "sectors" are 4K */ { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, -- cgit v1.2.3 From 04868a67ed582e845bf14a5a7789f8bdb3faadd3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Aug 2015 16:04:21 +0300 Subject: mtd: denali: hide core part from user in Kconfig There is no need to user to see the core part of the driver. Signed-off-by: Andy Shevchenko Signed-off-by: Brian Norris --- drivers/mtd/nand/Kconfig | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5b2806a7e5f7..3324281d1f53 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -42,23 +42,20 @@ config MTD_SM_COMMON default n config MTD_NAND_DENALI - tristate "Support Denali NAND controller" - depends on HAS_DMA - help - Enable support for the Denali NAND controller. This should be - combined with either the PCI or platform drivers to provide device - registration. + tristate config MTD_NAND_DENALI_PCI tristate "Support Denali NAND controller on Intel Moorestown" - depends on PCI && MTD_NAND_DENALI + select MTD_NAND_DENALI + depends on HAS_DMA && PCI help Enable the driver for NAND flash on Intel Moorestown, using the Denali NAND controller core. config MTD_NAND_DENALI_DT tristate "Support Denali NAND controller as a DT device" - depends on HAVE_CLK && MTD_NAND_DENALI + select MTD_NAND_DENALI + depends on HAS_DMA && HAVE_CLK help Enable the driver for NAND flash on platforms using a Denali NAND controller as a DT device. -- cgit v1.2.3 From 2445d33d852320d4ce700b2428d60991f8cf478f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Aug 2015 16:04:22 +0300 Subject: mtd: denali_pci: use module_pci_driver() macro Let's use module_pci_driver() macro to reduce code base of the driver. There is no functional change. Signed-off-by: Andy Shevchenko Signed-off-by: Brian Norris --- drivers/mtd/nand/denali_pci.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c index 6e2f387b823f..29e5d0c7d2d3 100644 --- a/drivers/mtd/nand/denali_pci.c +++ b/drivers/mtd/nand/denali_pci.c @@ -129,14 +129,4 @@ static struct pci_driver denali_pci_driver = { .remove = denali_pci_remove, }; -static int denali_init_pci(void) -{ - return pci_register_driver(&denali_pci_driver); -} -module_init(denali_init_pci); - -static void denali_exit_pci(void) -{ - pci_unregister_driver(&denali_pci_driver); -} -module_exit(denali_exit_pci); +module_pci_driver(denali_pci_driver); -- cgit v1.2.3 From add243d5bc371eef66f81c9da4fd4b55a18dad23 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Aug 2015 16:04:23 +0300 Subject: mtd: denali_pci: refactor driver using devres API In recent kernels we have a lot of helper functions, including devres API, to make life of device driver developer easy. Convert the driver using devm_kzalloc() and pcim_enable_device(). Signed-off-by: Andy Shevchenko Signed-off-by: Brian Norris --- drivers/mtd/nand/denali_pci.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c index 29e5d0c7d2d3..ad16e2ef1967 100644 --- a/drivers/mtd/nand/denali_pci.c +++ b/drivers/mtd/nand/denali_pci.c @@ -30,19 +30,19 @@ MODULE_DEVICE_TABLE(pci, denali_pci_ids); static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { - int ret = -ENODEV; + int ret; resource_size_t csr_base, mem_base; unsigned long csr_len, mem_len; struct denali_nand_info *denali; - denali = kzalloc(sizeof(*denali), GFP_KERNEL); + denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL); if (!denali) return -ENOMEM; - ret = pci_enable_device(dev); + ret = pcim_enable_device(dev); if (ret) { pr_err("Spectra: pci_enable_device failed.\n"); - goto failed_alloc_memery; + return ret; } if (id->driver_data == INTEL_CE4100) { @@ -70,14 +70,13 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ret = pci_request_regions(dev, DENALI_NAND_NAME); if (ret) { pr_err("Spectra: Unable to request memory regions\n"); - goto failed_enable_dev; + return ret; } denali->flash_reg = ioremap_nocache(csr_base, csr_len); if (!denali->flash_reg) { pr_err("Spectra: Unable to remap memory region\n"); - ret = -ENOMEM; - goto failed_req_regions; + return -ENOMEM; } denali->flash_mem = ioremap_nocache(mem_base, mem_len); @@ -99,13 +98,6 @@ failed_remap_mem: iounmap(denali->flash_mem); failed_remap_reg: iounmap(denali->flash_reg); -failed_req_regions: - pci_release_regions(dev); -failed_enable_dev: - pci_disable_device(dev); -failed_alloc_memery: - kfree(denali); - return ret; } @@ -117,9 +109,6 @@ static void denali_pci_remove(struct pci_dev *dev) denali_remove(denali); iounmap(denali->flash_reg); iounmap(denali->flash_mem); - pci_release_regions(dev); - pci_disable_device(dev); - kfree(denali); } static struct pci_driver denali_pci_driver = { -- cgit v1.2.3 From af83a67cad1452d48ecd77792f9218f26c445650 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 6 Aug 2015 16:04:24 +0300 Subject: mtd: denali_pci: switch to dev_err() It is better to have device name prefixed the actual error message. Signed-off-by: Andy Shevchenko Signed-off-by: Brian Norris --- drivers/mtd/nand/denali_pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c index ad16e2ef1967..de31514df282 100644 --- a/drivers/mtd/nand/denali_pci.c +++ b/drivers/mtd/nand/denali_pci.c @@ -41,7 +41,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ret = pcim_enable_device(dev); if (ret) { - pr_err("Spectra: pci_enable_device failed.\n"); + dev_err(&dev->dev, "Spectra: pci_enable_device failed.\n"); return ret; } @@ -69,19 +69,19 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ret = pci_request_regions(dev, DENALI_NAND_NAME); if (ret) { - pr_err("Spectra: Unable to request memory regions\n"); + dev_err(&dev->dev, "Spectra: Unable to request memory regions\n"); return ret; } denali->flash_reg = ioremap_nocache(csr_base, csr_len); if (!denali->flash_reg) { - pr_err("Spectra: Unable to remap memory region\n"); + dev_err(&dev->dev, "Spectra: Unable to remap memory region\n"); return -ENOMEM; } denali->flash_mem = ioremap_nocache(mem_base, mem_len); if (!denali->flash_mem) { - pr_err("Spectra: ioremap_nocache failed!"); + dev_err(&dev->dev, "Spectra: ioremap_nocache failed!"); ret = -ENOMEM; goto failed_remap_reg; } -- cgit v1.2.3 From 11c7e0e2f91fdfdf9f7b4d107bc93d6c298c95fe Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Fri, 14 Aug 2015 01:37:47 +0300 Subject: mtd: nettel: do not ignore mtd_device_register() failure in nettel_init() If mtd_device_register() fails in nettel_init(), iomap left mapped. The patch adds failure handling for mtd_device_register(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Brian Norris --- drivers/mtd/maps/nettel.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index eadcfffc4f9c..a577ef8553d0 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -385,20 +385,28 @@ static int __init nettel_init(void) } rc = mtd_device_register(intel_mtd, nettel_intel_partitions, num_intel_partitions); + if (rc) + goto out_map_destroy; #endif if (amd_mtd) { rc = mtd_device_register(amd_mtd, nettel_amd_partitions, num_amd_partitions); + if (rc) + goto out_mtd_unreg; } #ifdef CONFIG_MTD_CFI_INTELEXT register_reboot_notifier(&nettel_notifier_block); #endif - return(rc); + return rc; +out_mtd_unreg: #ifdef CONFIG_MTD_CFI_INTELEXT + mtd_device_unregister(intel_mtd); +out_map_destroy: + map_destroy(intel_mtd); out_unmap1: iounmap(nettel_intel_map.virt); #endif @@ -407,8 +415,7 @@ out_unmap2: iounmap(nettel_mmcrp); iounmap(nettel_amd_map.virt); - return(rc); - + return rc; } /****************************************************************************/ -- cgit v1.2.3 From 7e0c19c9608483808e6b5c294e357fa456f058e1 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 17 Jul 2015 11:37:52 +0100 Subject: mtd: physmap_of: fix null pointer deference when kzalloc returns null static analysis by smatch caught the following error: drivers/mtd/maps/physmap_of.c:135 of_get_probes() error: potential null dereference 'res'. (kzalloc returns null) Check for failed kzalloc and return -ENOMEM in of_flash_probe if this occurs. Signed-off-by: Colin Ian King Signed-off-by: Brian Norris --- drivers/mtd/maps/physmap_of.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 774b32fd29e6..3e614e9119d5 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -130,6 +130,8 @@ static const char * const *of_get_probes(struct device_node *dp) count++; res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL); + if (!res) + return NULL; count = 0; while (cplen > 0) { res[count] = cp; @@ -311,6 +313,10 @@ static int of_flash_probe(struct platform_device *dev) ppdata.of_node = dp; part_probe_types = of_get_probes(dp); + if (!part_probe_types) { + err = -ENOMEM; + goto err_out; + } mtd_device_parse_register(info->cmtd, part_probe_types, &ppdata, NULL, 0); of_free_probes(part_probe_types); -- cgit v1.2.3 From 0f0aca5d50ced8d8c18c7982a6b6332182b70e33 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 18 Aug 2015 21:03:13 +0800 Subject: mtd: omap_elm: Fix module alias Remove extra space after the "platform:" prefix and make the alias matches driver name. Signed-off-by: Axel Lin Acked-by: Roger Quadros Signed-off-by: Brian Norris --- drivers/mtd/nand/omap_elm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/omap_elm.c index 376bfe19104f..235ec7992b4c 100644 --- a/drivers/mtd/nand/omap_elm.c +++ b/drivers/mtd/nand/omap_elm.c @@ -574,5 +574,5 @@ module_platform_driver(elm_driver); MODULE_DESCRIPTION("ELM driver for BCH error correction"); MODULE_AUTHOR("Texas Instruments"); -MODULE_ALIAS("platform: elm"); +MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bc3e00f04cc1fe033a289c2fc2e5c73c0168d360 Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Tue, 18 Aug 2015 10:59:10 +0200 Subject: mtd: pxa3xx_nand: add a default chunk size When keeping the configuration set by the bootloader (by using the marvell,nand-keep-config property), the pxa3xx_nand_detect_config() function is called and set the chunk size to 512 as a default value if NDCR_PAGE_SZ is not set. In the other case, when not keeping the bootloader configuration, no chunk size is set. Fix this by adding a default chunk size of 512. Fixes: 70ed85232a93 ("mtd: nand: pxa3xx: Introduce multiple page I/O support") Signed-off-by: Antoine Tenart Acked-by: Robert Jarzmik Signed-off-by: Brian Norris --- drivers/mtd/nand/pxa3xx_nand.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 9d973cd5bcab..baf3246edeef 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1476,6 +1476,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) if (pdata->keep_config && !pxa3xx_nand_detect_config(info)) goto KEEP_CONFIG; + /* Set a default chunk size */ + info->chunk_size = 512; + ret = pxa3xx_nand_sensing(info); if (ret) { dev_info(&info->pdev->dev, "There is no chip on cs %d!\n", -- cgit v1.2.3 From 0b14392db2e998157d924085d7913e537ec26121 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Wed, 19 Aug 2015 20:30:14 +0200 Subject: mtd: nand: pxa3xx_nand: fix early spurious interrupt When the nand is first probe, and upon the first command start, the status bits should be cleared before the interrupts are unmasked. The bug is tricky : if the bootloader left a status bit set, the unmasking of interrupts does trigger the interrupt handler before the first command is issued, blocking the good behavior of the nand. The same would happen if in pxa3xx_nand code flow a status bit is left, and then a command is started. Signed-off-by: Robert Jarzmik Acked-by: Ezequiel Garcia Tested-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/nand/pxa3xx_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index baf3246edeef..faf056ba95b3 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -442,8 +442,8 @@ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) ndcr |= NDCR_ND_RUN; /* clear status bits and run */ - nand_writel(info, NDCR, 0); nand_writel(info, NDSR, NDSR_MASK); + nand_writel(info, NDCR, 0); nand_writel(info, NDCR, ndcr); } -- cgit v1.2.3 From 21fc0ef9652f0c809dc0d3e0a67f1e1bf6ff8255 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Wed, 19 Aug 2015 20:30:15 +0200 Subject: mtd: nand: pxa3xx-nand: fix random command timeouts When 2 commands are submitted in a row, and the second is very quick, the completion of the second command might never come. This happens especially if the second command is quick, such as a status read after an erase. The issue is that in the interrupt handler, the status bits are cleared after the new command is issued. There is a small temporal window where this happens : - the previous command has set the command done bit - the ready for a command bit is set - the handler submits the next command - just then, the command completes, and the command done bit is still set - the handler clears the "previous" command done bit - the handler exits In this flow, the "command done" of the next command will never trigger a new interrupt to finish the status command, as it was cleared for both commands. Fix this by clearing the status bit before submitting a new command. Signed-off-by: Robert Jarzmik Acked-by: Ezequiel Garcia Tested-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/nand/pxa3xx_nand.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index faf056ba95b3..51f8a58ed1a9 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -678,8 +678,14 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) is_ready = 1; } + /* + * Clear all status bit before issuing the next command, which + * can and will alter the status bits and will deserve a new + * interrupt on its own. This lets the controller exit the IRQ + */ + nand_writel(info, NDSR, status); + if (status & NDSR_WRCMDREQ) { - nand_writel(info, NDSR, NDSR_WRCMDREQ); status &= ~NDSR_WRCMDREQ; info->state = STATE_CMD_HANDLE; @@ -700,8 +706,6 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) nand_writel(info, NDCB0, info->ndcb3); } - /* clear NDSR to let the controller exit the IRQ */ - nand_writel(info, NDSR, status); if (is_completed) complete(&info->cmd_complete); if (is_ready) -- cgit v1.2.3 From b226eca2088004622434cbcc27c6401b64f22d7c Mon Sep 17 00:00:00 2001 From: Ezequiel García Date: Wed, 19 Aug 2015 19:40:09 -0300 Subject: nand: pxa3xx: Increase READ_ID buffer and make the size static The read ID count should be made as large as the maximum READ_ID size, so there's no need to have dynamic size. This commit sets the hardware maximum read ID count, which should be more than enough on all cases. Also, we get rid of the read_id_bytes, and use a macro instead. Signed-off-by: Ezequiel Garcia Acked-by: Robert Jarzmik Signed-off-by: Brian Norris --- drivers/mtd/nand/pxa3xx_nand.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 51f8a58ed1a9..740983a34626 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -129,6 +129,13 @@ #define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ #define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */ +/* + * This should be large enough to read 'ONFI' and 'JEDEC'. + * Let's use 7 bytes, which is the maximum ID count supported + * by the controller (see NDCR_RD_ID_CNT_MASK). + */ +#define READ_ID_BYTES 7 + /* macros for registers read/write */ #define nand_writel(info, off, val) \ writel_relaxed((val), (info)->mmio_base + (off)) @@ -176,8 +183,6 @@ struct pxa3xx_nand_host { /* calculated from pxa3xx_nand_flash data */ unsigned int col_addr_cycles; unsigned int row_addr_cycles; - size_t read_id_bytes; - }; struct pxa3xx_nand_info { @@ -917,7 +922,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, break; case NAND_CMD_READID: - info->buf_count = host->read_id_bytes; + info->buf_count = READ_ID_BYTES; info->ndcb0 |= NDCB0_CMD_TYPE(3) | NDCB0_ADDR_CYC(1) | command; @@ -1254,9 +1259,6 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, return -EINVAL; } - /* calculate flash information */ - host->read_id_bytes = (f->page_size == 2048) ? 4 : 2; - /* calculate addressing information */ host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; @@ -1272,7 +1274,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0; ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; - ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes); + ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); ndcr |= NDCR_SPARE_EN; /* enable spare by default */ info->reg_ndcr = ndcr; @@ -1283,23 +1285,10 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) { - /* - * We set 0 by hard coding here, for we don't support keep_config - * when there is more than one chip attached to the controller - */ - struct pxa3xx_nand_host *host = info->host[0]; uint32_t ndcr = nand_readl(info, NDCR); - if (ndcr & NDCR_PAGE_SZ) { - /* Controller's FIFO size */ - info->chunk_size = 2048; - host->read_id_bytes = 4; - } else { - info->chunk_size = 512; - host->read_id_bytes = 2; - } - /* Set an initial chunk size */ + info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; info->reg_ndcr = ndcr & ~NDCR_INT_MASK; info->ndtr0cs0 = nand_readl(info, NDTR0CS0); info->ndtr1cs0 = nand_readl(info, NDTR1CS0); -- cgit v1.2.3 From d1d97b76c4af41c8e836e73742c91cbf97d7483c Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 20 Aug 2015 09:07:16 +0200 Subject: mtd: dataflash: Export OF module alias information The SPI core currently reports the MODALIAS uevent as "spi:" even for SPI devices that were registered by OF. That means the OF module alias exported by MODULE_OF_TABLE(of,...) is currently not used and user-space has no way to autoload this module. But it is still a good practice to add the OF module alias information into the kernel module even when it currently is unused so once the SPI core is changed to report a correct OF modalias uevent, module autoloading will be working for this driver. Signed-off-by: Javier Martinez Canillas Signed-off-by: Brian Norris --- drivers/mtd/devices/mtd_dataflash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 0099aba72a8b..df6f61137376 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -102,6 +102,7 @@ static const struct of_device_id dataflash_dt_ids[] = { { .compatible = "atmel,dataflash", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, dataflash_dt_ids); #endif /* ......................................................................... */ -- cgit v1.2.3 From 0cb850486048ba4f64482a9d3e33dff47df34c79 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 31 Dec 2014 13:58:53 +0100 Subject: mtd: nand: add Toshiba TC58NVG0S3E to nand_ids table Add the full description of the Toshiba TC58NVG0S3E NAND chip in the nand_ids table so that we can later use the NAND ECC info and ONFI timing mode in controller drivers. Tested with asm9260_nand driver. [Brian: driver still under review] Signed-off-by: Oleksij Rempel Reviewed-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_ids.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 7124400d903b..a8804a3da076 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -29,6 +29,10 @@ struct nand_flash_dev nand_flash_ids[] = { * listed by full ID. We list them first so that we can easily identify * the most specific match. */ + {"TC58NVG0S3E 1G 3.3V 8-bit", + { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} }, + SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), + 2 }, {"TC58NVG2S0F 4G 3.3V 8-bit", { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, -- cgit v1.2.3 From 06bb6f5a69dfc53b79dd5f7afabdcd070a18afbf Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 10 Aug 2015 21:39:03 +0200 Subject: mtd: spi-nor: stop (ab)using struct spi_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using struct spi_device_id for storing list of flash devices comes from early SPI NOR framework days. Thanks to the commit 70f3ce0510af ("mtd: spi-nor: make spi_nor_scan() take a chip type name, not spi_device_id") we can stop using spi_device_id and just switch to our own struct. Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 62 +++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 47df4b5eae2f..c27d427fead4 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -29,6 +29,8 @@ #define SPI_NOR_MAX_ID_LEN 6 struct flash_info { + char *name; + /* * This array stores the ID bytes. * The first three bytes are the JEDIC ID. @@ -59,7 +61,7 @@ struct flash_info { #define JEDEC_MFR(info) ((info)->id[0]) -static const struct spi_device_id *spi_nor_match_id(const char *name); +static const struct flash_info *spi_nor_match_id(const char *name); /* * Read the status register, returning its value in the location @@ -169,7 +171,7 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) } /* Enable/disable 4-byte addressing mode. */ -static inline int set_4byte(struct spi_nor *nor, struct flash_info *info, +static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, int enable) { int status; @@ -469,7 +471,6 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) /* Used when the "_ext_id" is two bytes at most */ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ - ((kernel_ulong_t)&(struct flash_info) { \ .id = { \ ((_jedec_id) >> 16) & 0xff, \ ((_jedec_id) >> 8) & 0xff, \ @@ -481,11 +482,9 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) .sector_size = (_sector_size), \ .n_sectors = (_n_sectors), \ .page_size = 256, \ - .flags = (_flags), \ - }) + .flags = (_flags), #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ - ((kernel_ulong_t)&(struct flash_info) { \ .id = { \ ((_jedec_id) >> 16) & 0xff, \ ((_jedec_id) >> 8) & 0xff, \ @@ -498,17 +497,14 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) .sector_size = (_sector_size), \ .n_sectors = (_n_sectors), \ .page_size = 256, \ - .flags = (_flags), \ - }) + .flags = (_flags), #define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ - ((kernel_ulong_t)&(struct flash_info) { \ .sector_size = (_sector_size), \ .n_sectors = (_n_sectors), \ .page_size = (_page_size), \ .addr_width = (_addr_width), \ - .flags = (_flags), \ - }) + .flags = (_flags), /* NOTE: double check command sets and memory organization when you add * more nor chips. This current list focusses on newer chips, which @@ -521,7 +517,7 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) * For historical (and compatibility) reasons (before we got above config) some * old entries may be missing 4K flag. */ -static const struct spi_device_id spi_nor_ids[] = { +static const struct flash_info spi_nor_ids[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, @@ -703,11 +699,11 @@ static const struct spi_device_id spi_nor_ids[] = { { }, }; -static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) +static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) { int tmp; u8 id[SPI_NOR_MAX_ID_LEN]; - struct flash_info *info; + const struct flash_info *info; tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); if (tmp < 0) { @@ -716,7 +712,7 @@ static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) } for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { - info = (void *)spi_nor_ids[tmp].driver_data; + info = &spi_nor_ids[tmp]; if (info->id_len) { if (!memcmp(info->id, id, info->id_len)) return &spi_nor_ids[tmp]; @@ -962,7 +958,7 @@ static int micron_quad_enable(struct spi_nor *nor) return 0; } -static int set_quad_mode(struct spi_nor *nor, struct flash_info *info) +static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) { int status; @@ -1004,8 +1000,7 @@ static int spi_nor_check(struct spi_nor *nor) int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) { - const struct spi_device_id *id = NULL; - struct flash_info *info; + const struct flash_info *info = NULL; struct device *dev = nor->dev; struct mtd_info *mtd = nor->mtd; struct device_node *np = dev->of_node; @@ -1017,26 +1012,24 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) return ret; if (name) - id = spi_nor_match_id(name); + info = spi_nor_match_id(name); /* Try to auto-detect if chip name wasn't specified or not found */ - if (!id) - id = spi_nor_read_id(nor); - if (IS_ERR_OR_NULL(id)) + if (!info) + info = spi_nor_read_id(nor); + if (IS_ERR_OR_NULL(info)) return -ENOENT; - info = (void *)id->driver_data; - /* * If caller has specified name of flash model that can normally be * detected using JEDEC, let's verify it. */ if (name && info->id_len) { - const struct spi_device_id *jid; + const struct flash_info *jinfo; - jid = spi_nor_read_id(nor); - if (IS_ERR(jid)) { - return PTR_ERR(jid); - } else if (jid != id) { + jinfo = spi_nor_read_id(nor); + if (IS_ERR(jinfo)) { + return PTR_ERR(jinfo); + } else if (jinfo != info) { /* * JEDEC knows better, so overwrite platform ID. We * can't trust partitions any longer, but we'll let @@ -1045,9 +1038,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) * information, even if it's not 100% accurate. */ dev_warn(dev, "found %s, expected %s\n", - jid->name, id->name); - id = jid; - info = (void *)jid->driver_data; + jinfo->name, info->name); + info = jinfo; } } @@ -1197,7 +1189,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) nor->read_dummy = spi_nor_read_dummy_cycles(nor); - dev_info(dev, "%s (%lld Kbytes)\n", id->name, + dev_info(dev, "%s (%lld Kbytes)\n", info->name, (long long)mtd->size >> 10); dev_dbg(dev, @@ -1220,9 +1212,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) } EXPORT_SYMBOL_GPL(spi_nor_scan); -static const struct spi_device_id *spi_nor_match_id(const char *name) +static const struct flash_info *spi_nor_match_id(const char *name) { - const struct spi_device_id *id = spi_nor_ids; + const struct flash_info *id = spi_nor_ids; while (id->name[0]) { if (!strcmp(name, id->name)) -- cgit v1.2.3 From cc7fce80229067890365c1ee196be5d304d36dea Mon Sep 17 00:00:00 2001 From: Tomer Barletz Date: Tue, 4 Aug 2015 21:00:24 -0700 Subject: mtd: blkdevs: fix switch-bool compilation warning With gcc 5.1 I get: warning: switch condition has boolean value [-Wswitch-bool] Signed-off-by: Tomer Barletz Signed-off-by: Brian Norris --- drivers/mtd/mtd_blkdevs.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 41acc507b22e..88304751eb8a 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -97,14 +97,13 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, if (req->cmd_flags & REQ_DISCARD) return tr->discard(dev, block, nsect); - switch(rq_data_dir(req)) { - case READ: + if (rq_data_dir(req) == READ) { for (; nsect > 0; nsect--, block++, buf += tr->blksize) if (tr->readsect(dev, block, buf)) return -EIO; rq_flush_dcache_pages(req); return 0; - case WRITE: + } else { if (!tr->writesect) return -EIO; @@ -113,9 +112,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, if (tr->writesect(dev, block, buf)) return -EIO; return 0; - default: - printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); - return -EIO; } } -- cgit v1.2.3 From 718e38b4d96085b4dab08d3d32e5e033aa1ba6e9 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 8 Jul 2015 14:50:19 +0300 Subject: mtd: mtd_oobtest: Fix the address offset with vary_offset case When vary_offset is set (e.g. test case 3), the offset is not always zero so memcmpshow() will show the wrong offset in the print message. To fix this we introduce a new function memcmpshowoffset() which takes offset as a parameter and displays the right offset and use it in the case where offset is non zero. The old memcmpshow() functionality is preserved by converting it into a macro with offset preset to 0. Signed-off-by: Roger Quadros Signed-off-by: Brian Norris --- drivers/mtd/tests/oobtest.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/tests/oobtest.c b/drivers/mtd/tests/oobtest.c index 8e8525f0202f..31762120eb56 100644 --- a/drivers/mtd/tests/oobtest.c +++ b/drivers/mtd/tests/oobtest.c @@ -125,7 +125,8 @@ static int write_whole_device(void) * Display the address, offset and data bytes at comparison failure. * Return number of bitflips encountered. */ -static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t count) +static size_t memcmpshowoffset(loff_t addr, loff_t offset, const void *cs, + const void *ct, size_t count) { const unsigned char *su1, *su2; int res; @@ -135,8 +136,9 @@ static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t cou for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) { res = *su1 ^ *su2; if (res) { - pr_info("error @addr[0x%lx:0x%zx] 0x%x -> 0x%x diff 0x%x\n", - (unsigned long)addr, i, *su1, *su2, res); + pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0x%x diff 0x%x\n", + (unsigned long)addr, (unsigned long)offset + i, + *su1, *su2, res); bitflips += hweight8(res); } } @@ -144,6 +146,9 @@ static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t cou return bitflips; } +#define memcmpshow(addr, cs, ct, count) memcmpshowoffset((addr), 0, (cs), (ct),\ + (count)) + /* * Compare with 0xff and show the address, offset and data bytes at * comparison failure. Return number of bitflips encountered. @@ -228,9 +233,10 @@ static int verify_eraseblock(int ebnum) errcnt += 1; return err ? err : -1; } - bitflips = memcmpshow(addr, readbuf + use_offset, - writebuf + (use_len_max * i) + use_offset, - use_len); + bitflips = memcmpshowoffset(addr, use_offset, + readbuf + use_offset, + writebuf + (use_len_max * i) + use_offset, + use_len); /* verify pre-offset area for 0xff */ bitflips += memffshow(addr, 0, readbuf, use_offset); -- cgit v1.2.3