diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-25 13:35:34 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-25 13:35:34 -0700 |
commit | e35a4a4e13c35f500a8d38e836b5e335c7515494 (patch) | |
tree | 32940f68475c16ddee8ed2933c9a4ae6e9b44265 /drivers/mtd/nand/raw | |
parent | 8eb48fc7c54ed627a693a205570f0eceea64274c (diff) | |
parent | 6cadd424abb63120f8346a4509dc43bddc9401d3 (diff) | |
download | linux-e35a4a4e13c35f500a8d38e836b5e335c7515494.tar.bz2 |
Merge tag 'mtd/changes-for-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
Pull MTD updates from Miquel Raynal:
"There has been a lot of activity in the MTD subsystem recently, with a
number of SPI-NOR cleanups as well as the introduction of ECC engines
that can be used by SPI controllers (hence a few SPI patches in here).
Core MTD changes:
- Replace the expert mode symbols with a single helper
- Fix misuses of of_match_ptr()
- Remove partid and partname debugfs files
- tests: Fix eraseblock read speed miscalculation for lower partition
sizes
- TRX parser: Allow to use on MediaTek MIPS SoCs
MTD driver changes:
- spear_smi: use GFP_KERNEL
- mchp48l640: Add SPI ID table
- mchp23k256: Add SPI ID table
- blkdevs: Avoid soft lockups with some mtd/spi devices
- aspeed-smc: Improve probe resilience
Hyperbus changes:
- HBMC_AM654 should depend on ARCH_K3
NAND core changes:
- ECC:
- Add infrastructure to support hardware engines
- Add a new helper to retrieve the ECC context
- Provide a helper to retrieve a pilelined engine device
NAND-ECC changes:
- Macronix ECC engine:
- Add Macronix external ECC engine support
- Support SPI pipelined mode
- Make two read-only arrays static const
- Fix compile test issue
Raw NAND core changes:
- Fix misuses of of_match_node()
- Rework of_get_nand_bus_width()
- Remove of_get_nand_on_flash_bbt() wrapper
- Protect access to rawnand devices while in suspend
- bindings: Document the wp-gpios property
Rax NAND controller driver changes:
- atmel: Fix refcount issue in atmel_nand_controller_init
- nandsim:
- Add NS_PAGE_BYTE_SHIFT macro to replace the repeat pattern
- Merge repeat codes in ns_switch_state
- Replace overflow check with kzalloc to single kcalloc
- rockchip: Fix platform_get_irq.cocci warning
- stm32_fmc2: Add NAND Write Protect support
- pl353: Set the nand chip node as the flash node
- brcmnand: Fix sparse warnings in bcma_nand
- omap_elm: Remove redundant variable 'errors'
- gpmi:
- Support fast edo timings for mx28
- Validate controller clock rate
- Fix controller timings setting
- brcmnand:
- Add BCMA shim
- BCMA controller uses command shift of 0
- Allow platform data instantation
- Add platform data structure for BCMA
- Allow working without interrupts
- Move OF operations out of brcmnand_init_cs()
- Avoid pdev in brcmnand_init_cs()
- Allow SoC to provide I/O operations
- Assign soc as early as possible
Onenand changes:
- Check for error irq
SPI-NAND core changes:
- Delay a little bit the dirmap creation
- Create direct mapping descriptors for ECC operations
SPI-NAND driver changes:
- macronix: Use random program load
SPI NOR core changes:
- Move vendor specific code out of the core into vendor drivers.
- Unify all function and object names in the vendor modules.
- Make setup() callback optional to improve readability.
- Skip erase logic when the SPI_NOR_NO_ERASE flag is set at flash
declaration.
SPI changes:
- Macronix SPI controller:
- Fix the transmit path
- Create a helper to configure the controller before an operation
- Create a helper to ease the start of an operation
- Add support for direct mapping
- Add support for pipelined ECC operations
- spi-mem:
- Introduce a capability structure
- Check the controller extra capabilities
- cadence-quadspi/mxic: Provide capability structures
- Kill the spi_mem_dtr_supports_op() helper
- Add an ecc parameter to the spi_mem_op structure
Binding changes:
- Dropped mtd/cortina,gemini-flash.txt
- Convert BCM47xx partitions to json-schema
- Vendor prefixes: Clarify Macronix prefix
- SPI NAND: Convert spi-nand description file to yaml
- Raw NAND chip: Create a NAND chip description
- Raw NAND controller:
- Harmonize the property types
- Fix a comment in the examples
- Fix the reg property description
- Describe Macronix NAND ECC engine
- Macronix SPI controller:
- Document the nand-ecc-engine property
- Convert to yaml
- The interrupt property is not mandatory"
* tag 'mtd/changes-for-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (104 commits)
mtd: nand: ecc: mxic: Fix compile test issue
mtd: nand: mxic-ecc: make two read-only arrays static const
mtd: hyperbus: HBMC_AM654 should depend on ARCH_K3
mtd: core: Remove partid and partname debugfs files
dt-bindings: mtd: partitions: convert BCM47xx to the json-schema
mtd: tests: Fix eraseblock read speed miscalculation for lower partition sizes
mtd: rawnand: atmel: fix refcount issue in atmel_nand_controller_init
mtd: rawnand: rockchip: fix platform_get_irq.cocci warning
mtd: spi-nor: Skip erase logic when SPI_NOR_NO_ERASE is set
mtd: spi-nor: renumber flags
mtd: spi-nor: slightly change code style in spi_nor_sr_ready()
mtd: spi-nor: spansion: rename vendor specific functions and defines
mtd: spi-nor: spansion: convert USE_CLSR to a manufacturer flag
mtd: spi-nor: move all spansion specifics into spansion.c
mtd: spi-nor: spansion: slightly rework control flow in late_init()
mtd: spi-nor: micron-st: rename vendor specific functions and defines
mtd: spi-nor: micron-st: convert USE_FSR to a manufacturer flag
mtd: spi-nor: move all micron-st specifics into micron-st.c
mtd: spi-nor: xilinx: correct the debug message
mtd: spi-nor: xilinx: rename vendor specific functions and defines
...
Diffstat (limited to 'drivers/mtd/nand/raw')
21 files changed, 428 insertions, 150 deletions
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 820e5dc3bc9b..9b078e78f3fa 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -209,6 +209,19 @@ config MTD_NAND_BRCMNAND originally designed for Set-Top Box but is used on various BCM7xxx, BCM3xxx, BCM63xxx, iProc/Cygnus and more. +if MTD_NAND_BRCMNAND + +config MTD_NAND_BRCMNAND_BCMA + tristate "Broadcom BCMA NAND controller" + depends on BCMA_NFLASH + depends on BCMA + help + Enables the BRCMNAND controller over BCMA on BCM47186/BCM5358 SoCs. + The glue driver will take care of performing the low-level I/O + operations to interface the BRCMNAND controller over the BCMA bus. + +endif # MTD_NAND_BRCMNAND + config MTD_NAND_BCM47XXNFLASH tristate "BCM4706 BCMA NAND controller" depends on BCMA_NFLASH diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index f3276ee9e4fe..6ef14442c71a 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -1938,7 +1938,7 @@ static const struct atmel_smc_nand_ebi_csa_cfg sam9x60_ebi_csa = { .nfd0_on_d16 = AT91_SFR_CCFG_NFD0_ON_D16, }; -static const struct of_device_id atmel_ebi_csa_regmap_of_ids[] = { +static const struct of_device_id __maybe_unused atmel_ebi_csa_regmap_of_ids[] = { { .compatible = "atmel,at91sam9260-matrix", .data = &at91sam9260_ebi_csa, @@ -2060,13 +2060,15 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, nc->mck = of_clk_get(dev->parent->of_node, 0); if (IS_ERR(nc->mck)) { dev_err(dev, "Failed to retrieve MCK clk\n"); - return PTR_ERR(nc->mck); + ret = PTR_ERR(nc->mck); + goto out_release_dma; } np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); if (!np) { dev_err(dev, "Missing or invalid atmel,smc property\n"); - return -EINVAL; + ret = -EINVAL; + goto out_release_dma; } nc->smc = syscon_node_to_regmap(np); @@ -2074,10 +2076,16 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, if (IS_ERR(nc->smc)) { ret = PTR_ERR(nc->smc); dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret); - return ret; + goto out_release_dma; } return 0; + +out_release_dma: + if (nc->dmac) + dma_release_channel(nc->dmac); + + return ret; } static int @@ -2648,7 +2656,7 @@ static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL, static struct platform_driver atmel_nand_controller_driver = { .driver = { .name = "atmel-nand-controller", - .of_match_table = of_match_ptr(atmel_nand_controller_of_ids), + .of_match_table = atmel_nand_controller_of_ids, .pm = &atmel_nand_controller_pm_ops, }, .probe = atmel_nand_controller_probe, diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c index 498e41ccabbd..4d7dc8a9c373 100644 --- a/drivers/mtd/nand/raw/atmel/pmecc.c +++ b/drivers/mtd/nand/raw/atmel/pmecc.c @@ -920,7 +920,7 @@ static struct atmel_pmecc_caps sama5d2_caps = { .correct_erased_chunks = true, }; -static const struct of_device_id atmel_pmecc_legacy_match[] = { +static const struct of_device_id __maybe_unused atmel_pmecc_legacy_match[] = { { .compatible = "atmel,sama5d4-nand", &sama5d4_caps }, { .compatible = "atmel,sama5d2-nand", &sama5d2_caps }, { /* sentinel */ } @@ -1003,7 +1003,7 @@ static int atmel_pmecc_probe(struct platform_device *pdev) static struct platform_driver atmel_pmecc_driver = { .driver = { .name = "atmel-pmecc", - .of_match_table = of_match_ptr(atmel_pmecc_match), + .of_match_table = atmel_pmecc_match, }, .probe = atmel_pmecc_probe, }; diff --git a/drivers/mtd/nand/raw/brcmnand/Makefile b/drivers/mtd/nand/raw/brcmnand/Makefile index 195b845e48b8..16dc7254200e 100644 --- a/drivers/mtd/nand/raw/brcmnand/Makefile +++ b/drivers/mtd/nand/raw/brcmnand/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63138_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm6368_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o + +obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMA) += bcma_nand.o diff --git a/drivers/mtd/nand/raw/brcmnand/bcma_nand.c b/drivers/mtd/nand/raw/brcmnand/bcma_nand.c new file mode 100644 index 000000000000..dd27977919fb --- /dev/null +++ b/drivers/mtd/nand/raw/brcmnand/bcma_nand.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright © 2021 Broadcom + */ +#include <linux/bcma/bcma.h> +#include <linux/bcma/bcma_driver_chipcommon.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "brcmnand.h" + +struct brcmnand_bcma_soc { + struct brcmnand_soc soc; + struct bcma_drv_cc *cc; +}; + +static inline bool brcmnand_bcma_needs_swapping(u32 offset) +{ + switch (offset) { + case BCMA_CC_NAND_SPARE_RD0: + case BCMA_CC_NAND_SPARE_RD4: + case BCMA_CC_NAND_SPARE_RD8: + case BCMA_CC_NAND_SPARE_RD12: + case BCMA_CC_NAND_SPARE_WR0: + case BCMA_CC_NAND_SPARE_WR4: + case BCMA_CC_NAND_SPARE_WR8: + case BCMA_CC_NAND_SPARE_WR12: + case BCMA_CC_NAND_DEVID: + case BCMA_CC_NAND_DEVID_X: + case BCMA_CC_NAND_SPARE_RD16: + case BCMA_CC_NAND_SPARE_RD20: + case BCMA_CC_NAND_SPARE_RD24: + case BCMA_CC_NAND_SPARE_RD28: + return true; + } + + return false; +} + +static inline struct brcmnand_bcma_soc *to_bcma_soc(struct brcmnand_soc *soc) +{ + return container_of(soc, struct brcmnand_bcma_soc, soc); +} + +static u32 brcmnand_bcma_read_reg(struct brcmnand_soc *soc, u32 offset) +{ + struct brcmnand_bcma_soc *sc = to_bcma_soc(soc); + u32 val; + + /* Offset into the NAND block and deal with the flash cache separately */ + if (offset == BRCMNAND_NON_MMIO_FC_ADDR) + offset = BCMA_CC_NAND_CACHE_DATA; + else + offset += BCMA_CC_NAND_REVISION; + + val = bcma_cc_read32(sc->cc, offset); + + /* Swap if necessary */ + if (brcmnand_bcma_needs_swapping(offset)) + val = be32_to_cpu((__force __be32)val); + return val; +} + +static void brcmnand_bcma_write_reg(struct brcmnand_soc *soc, u32 val, + u32 offset) +{ + struct brcmnand_bcma_soc *sc = to_bcma_soc(soc); + + /* Offset into the NAND block */ + if (offset == BRCMNAND_NON_MMIO_FC_ADDR) + offset = BCMA_CC_NAND_CACHE_DATA; + else + offset += BCMA_CC_NAND_REVISION; + + /* Swap if necessary */ + if (brcmnand_bcma_needs_swapping(offset)) + val = (__force u32)cpu_to_be32(val); + + bcma_cc_write32(sc->cc, offset, val); +} + +static struct brcmnand_io_ops brcmnand_bcma_io_ops = { + .read_reg = brcmnand_bcma_read_reg, + .write_reg = brcmnand_bcma_write_reg, +}; + +static void brcmnand_bcma_prepare_data_bus(struct brcmnand_soc *soc, bool prepare, + bool is_param) +{ + struct brcmnand_bcma_soc *sc = to_bcma_soc(soc); + + /* Reset the cache address to ensure we are already accessing the + * beginning of a sub-page. + */ + bcma_cc_write32(sc->cc, BCMA_CC_NAND_CACHE_ADDR, 0); +} + +static int brcmnand_bcma_nand_probe(struct platform_device *pdev) +{ + struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev); + struct brcmnand_bcma_soc *soc; + + soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL); + if (!soc) + return -ENOMEM; + + soc->cc = container_of(nflash, struct bcma_drv_cc, nflash); + soc->soc.prepare_data_bus = brcmnand_bcma_prepare_data_bus; + soc->soc.ops = &brcmnand_bcma_io_ops; + + if (soc->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { + dev_err(&pdev->dev, "Use bcm47xxnflash for 4706!\n"); + return -ENODEV; + } + + return brcmnand_probe(pdev, &soc->soc); +} + +static struct platform_driver brcmnand_bcma_nand_driver = { + .probe = brcmnand_bcma_nand_probe, + .remove = brcmnand_remove, + .driver = { + .name = "bcma_brcmnand", + .pm = &brcmnand_pm_ops, + } +}; +module_platform_driver(brcmnand_bcma_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("NAND controller driver glue for BCMA chips"); diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index aee78f5f4f15..2e9c2e2d9c9f 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -9,6 +9,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/platform_device.h> +#include <linux/platform_data/brcmnand.h> #include <linux/err.h> #include <linux/completion.h> #include <linux/interrupt.h> @@ -25,6 +26,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/slab.h> +#include <linux/static_key.h> #include <linux/list.h> #include <linux/log2.h> @@ -207,13 +209,15 @@ enum { struct brcmnand_host; +static DEFINE_STATIC_KEY_FALSE(brcmnand_soc_has_ops_key); + struct brcmnand_controller { struct device *dev; struct nand_controller controller; void __iomem *nand_base; void __iomem *nand_fc; /* flash cache */ void __iomem *flash_dma_base; - unsigned int irq; + int irq; unsigned int dma_irq; int nand_version; @@ -592,15 +596,29 @@ enum { INTFC_CTLR_READY = BIT(31), }; +static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl) +{ +#if IS_ENABLED(CONFIG_MTD_NAND_BRCMNAND_BCMA) + return static_branch_unlikely(&brcmnand_soc_has_ops_key); +#else + return false; +#endif +} + static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) { + if (brcmnand_non_mmio_ops(ctrl)) + return brcmnand_soc_read(ctrl->soc, offs); return brcmnand_readl(ctrl->nand_base + offs); } static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs, u32 val) { - brcmnand_writel(val, ctrl->nand_base + offs); + if (brcmnand_non_mmio_ops(ctrl)) + brcmnand_soc_write(ctrl->soc, val, offs); + else + brcmnand_writel(val, ctrl->nand_base + offs); } static int brcmnand_revision_init(struct brcmnand_controller *ctrl) @@ -766,13 +784,18 @@ static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl, static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word) { + if (brcmnand_non_mmio_ops(ctrl)) + return brcmnand_soc_read(ctrl->soc, BRCMNAND_NON_MMIO_FC_ADDR); return __raw_readl(ctrl->nand_fc + word * 4); } static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, int word, u32 val) { - __raw_writel(val, ctrl->nand_fc + word * 4); + if (brcmnand_non_mmio_ops(ctrl)) + brcmnand_soc_write(ctrl->soc, val, BRCMNAND_NON_MMIO_FC_ADDR); + else + __raw_writel(val, ctrl->nand_fc + word * 4); } static inline void edu_writel(struct brcmnand_controller *ctrl, @@ -897,6 +920,12 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) { + /* Kludge for the BCMA-based NAND controller which does not actually + * shift the command + */ + if (ctrl->nand_version == 0x0304 && brcmnand_non_mmio_ops(ctrl)) + return 0; + if (ctrl->nand_version < 0x0602) return 24; return 0; @@ -1592,7 +1621,7 @@ static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip) bool err = false; int sts; - if (mtd->oops_panic_write) { + if (mtd->oops_panic_write || ctrl->irq < 0) { /* switch to interrupt polling and PIO mode */ disable_ctrl_irqs(ctrl); sts = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, @@ -2750,33 +2779,27 @@ static const struct nand_controller_ops brcmnand_controller_ops = { .attach_chip = brcmnand_attach_chip, }; -static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) +static int brcmnand_init_cs(struct brcmnand_host *host, + const char * const *part_probe_types) { struct brcmnand_controller *ctrl = host->ctrl; - struct platform_device *pdev = host->pdev; + struct device *dev = ctrl->dev; struct mtd_info *mtd; struct nand_chip *chip; int ret; u16 cfg_offs; - ret = of_property_read_u32(dn, "reg", &host->cs); - if (ret) { - dev_err(&pdev->dev, "can't get chip-select\n"); - return -ENXIO; - } - mtd = nand_to_mtd(&host->chip); chip = &host->chip; - nand_set_flash_node(chip, dn); nand_set_controller_data(chip, host); - mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d", + mtd->name = devm_kasprintf(dev, GFP_KERNEL, "brcmnand.%d", host->cs); if (!mtd->name) return -ENOMEM; mtd->owner = THIS_MODULE; - mtd->dev.parent = &pdev->dev; + mtd->dev.parent = dev; chip->legacy.cmd_ctrl = brcmnand_cmd_ctrl; chip->legacy.cmdfunc = brcmnand_cmdfunc; @@ -2810,7 +2833,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) if (ret) return ret; - ret = mtd_device_register(mtd, NULL, 0); + ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); if (ret) nand_cleanup(chip); @@ -2914,7 +2937,7 @@ const struct dev_pm_ops brcmnand_pm_ops = { }; EXPORT_SYMBOL_GPL(brcmnand_pm_ops); -static const struct of_device_id brcmnand_of_match[] = { +static const struct of_device_id __maybe_unused brcmnand_of_match[] = { { .compatible = "brcm,brcmnand-v2.1" }, { .compatible = "brcm,brcmnand-v2.2" }, { .compatible = "brcm,brcmnand-v4.0" }, @@ -2979,17 +3002,15 @@ static int brcmnand_edu_setup(struct platform_device *pdev) int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) { + struct brcmnand_platform_data *pd = dev_get_platdata(&pdev->dev); struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node, *child; struct brcmnand_controller *ctrl; + struct brcmnand_host *host; struct resource *res; int ret; - /* We only support device-tree instantiation */ - if (!dn) - return -ENODEV; - - if (!of_match_node(brcmnand_of_match, dn)) + if (dn && !of_match_node(brcmnand_of_match, dn)) return -ENODEV; ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); @@ -2998,6 +3019,13 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) dev_set_drvdata(dev, ctrl); ctrl->dev = dev; + ctrl->soc = soc; + + /* Enable the static key if the soc provides I/O operations indicating + * that a non-memory mapped IO access path must be used + */ + if (brcmnand_soc_has_ops(ctrl->soc)) + static_branch_enable(&brcmnand_soc_has_ops_key); init_completion(&ctrl->done); init_completion(&ctrl->dma_done); @@ -3009,7 +3037,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) /* NAND register range */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ctrl->nand_base = devm_ioremap_resource(dev, res); - if (IS_ERR(ctrl->nand_base)) + if (IS_ERR(ctrl->nand_base) && !brcmnand_soc_has_ops(soc)) return PTR_ERR(ctrl->nand_base); /* Enable clock before using NAND registers */ @@ -3126,40 +3154,33 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) } /* IRQ */ - ctrl->irq = platform_get_irq(pdev, 0); - if ((int)ctrl->irq < 0) { - dev_err(dev, "no IRQ defined\n"); - ret = -ENODEV; - goto err; - } - - /* - * Some SoCs integrate this controller (e.g., its interrupt bits) in - * interesting ways - */ - if (soc) { - ctrl->soc = soc; - - ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0, - DRV_NAME, ctrl); + ctrl->irq = platform_get_irq_optional(pdev, 0); + if (ctrl->irq > 0) { + /* + * Some SoCs integrate this controller (e.g., its interrupt bits) in + * interesting ways + */ + if (soc) { + ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0, + DRV_NAME, ctrl); - /* Enable interrupt */ - ctrl->soc->ctlrdy_ack(ctrl->soc); - ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); - } else { - /* Use standard interrupt infrastructure */ - ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, - DRV_NAME, ctrl); - } - if (ret < 0) { - dev_err(dev, "can't allocate IRQ %d: error %d\n", - ctrl->irq, ret); - goto err; + /* Enable interrupt */ + ctrl->soc->ctlrdy_ack(ctrl->soc); + ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); + } else { + /* Use standard interrupt infrastructure */ + ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, + DRV_NAME, ctrl); + } + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->irq, ret); + goto err; + } } for_each_available_child_of_node(dn, child) { if (of_device_is_compatible(child, "brcm,nandcs")) { - struct brcmnand_host *host; host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); if (!host) { @@ -3170,7 +3191,16 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) host->pdev = pdev; host->ctrl = ctrl; - ret = brcmnand_init_cs(host, child); + ret = of_property_read_u32(child, "reg", &host->cs); + if (ret) { + dev_err(dev, "can't get chip-select\n"); + devm_kfree(dev, host); + continue; + } + + nand_set_flash_node(&host->chip, child); + + ret = brcmnand_init_cs(host, NULL); if (ret) { devm_kfree(dev, host); continue; /* Try all chip-selects */ @@ -3180,6 +3210,32 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) } } + if (!list_empty(&ctrl->host_list)) + return 0; + + if (!pd) { + ret = -ENODEV; + goto err; + } + + /* If we got there we must have been probing via platform data */ + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) { + ret = -ENOMEM; + goto err; + } + host->pdev = pdev; + host->ctrl = ctrl; + host->cs = pd->chip_select; + host->chip.ecc.size = pd->ecc_stepsize; + host->chip.ecc.strength = pd->ecc_strength; + + ret = brcmnand_init_cs(host, pd->part_probe_types); + if (ret) + goto err; + + list_add_tail(&host->node, &ctrl->host_list); + /* No chip-selects could initialize properly */ if (list_empty(&ctrl->host_list)) { ret = -ENODEV; diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h index eb498fbe505e..f1f93d85f50d 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.h +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h @@ -11,12 +11,25 @@ struct platform_device; struct dev_pm_ops; +struct brcmnand_io_ops; + +/* Special register offset constant to intercept a non-MMIO access + * to the flash cache register space. This is intentionally large + * not to overlap with an existing offset. + */ +#define BRCMNAND_NON_MMIO_FC_ADDR 0xffffffff struct brcmnand_soc { bool (*ctlrdy_ack)(struct brcmnand_soc *soc); void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare, bool is_param); + const struct brcmnand_io_ops *ops; +}; + +struct brcmnand_io_ops { + u32 (*read_reg)(struct brcmnand_soc *soc, u32 offset); + void (*write_reg)(struct brcmnand_soc *soc, u32 val, u32 offset); }; static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc, @@ -58,6 +71,22 @@ static inline void brcmnand_writel(u32 val, void __iomem *addr) writel_relaxed(val, addr); } +static inline bool brcmnand_soc_has_ops(struct brcmnand_soc *soc) +{ + return soc && soc->ops && soc->ops->read_reg && soc->ops->write_reg; +} + +static inline u32 brcmnand_soc_read(struct brcmnand_soc *soc, u32 offset) +{ + return soc->ops->read_reg(soc, offset); +} + +static inline void brcmnand_soc_write(struct brcmnand_soc *soc, u32 val, + u32 offset) +{ + soc->ops->write_reg(soc, val, offset); +} + int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc); int brcmnand_remove(struct platform_device *pdev); diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index ded4df473928..44b14c9dc9a7 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -644,10 +644,11 @@ err_out: * RDN_DELAY = ----------------------- {3} * RP */ -static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this, - const struct nand_sdr_timings *sdr) +static int gpmi_nfc_compute_timings(struct gpmi_nand_data *this, + const struct nand_sdr_timings *sdr) { struct gpmi_nfc_hardware_timing *hw = &this->hw; + struct resources *r = &this->resources; unsigned int dll_threshold_ps = this->devdata->max_chain_delay; unsigned int period_ps, reference_period_ps; unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles; @@ -656,21 +657,33 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this, int sample_delay_ps, sample_delay_factor; u16 busy_timeout_cycles; u8 wrn_dly_sel; + unsigned long clk_rate, min_rate; if (sdr->tRC_min >= 30000) { /* ONFI non-EDO modes [0-3] */ hw->clk_rate = 22000000; + min_rate = 0; wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; } else if (sdr->tRC_min >= 25000) { /* ONFI EDO mode 4 */ hw->clk_rate = 80000000; + min_rate = 22000000; wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; } else { /* ONFI EDO mode 5 */ hw->clk_rate = 100000000; + min_rate = 80000000; wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; } + clk_rate = clk_round_rate(r->clock[0], hw->clk_rate); + if (clk_rate <= min_rate) { + dev_err(this->dev, "clock setting: expected %ld, got %ld\n", + hw->clk_rate, clk_rate); + return -ENOTSUPP; + } + + hw->clk_rate = clk_rate; /* SDR core timings are given in picoseconds */ period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate); @@ -711,6 +724,7 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this, hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) | BM_GPMI_CTRL1_DLL_ENABLE | (use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0); + return 0; } static int gpmi_nfc_apply_timings(struct gpmi_nand_data *this) @@ -766,14 +780,15 @@ static int gpmi_setup_interface(struct nand_chip *chip, int chipnr, { struct gpmi_nand_data *this = nand_get_controller_data(chip); const struct nand_sdr_timings *sdr; + int ret; /* Retrieve required NAND timings */ sdr = nand_get_sdr_timings(conf); if (IS_ERR(sdr)) return PTR_ERR(sdr); - /* Only MX6 GPMI controller can reach EDO timings */ - if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this)) + /* Only MX28/MX6 GPMI controller can reach EDO timings */ + if (sdr->tRC_min <= 25000 && !GPMI_IS_MX28(this) && !GPMI_IS_MX6(this)) return -ENOTSUPP; /* Stop here if this call was just a check */ @@ -781,7 +796,9 @@ static int gpmi_setup_interface(struct nand_chip *chip, int chipnr, return 0; /* Do the actual derivation of the controller timings */ - gpmi_nfc_compute_timings(this, sdr); + ret = gpmi_nfc_compute_timings(this, sdr); + if (ret) + return ret; this->hw.must_apply_timings = true; diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c index b18861bdcdc8..ff26c10f295d 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c @@ -567,7 +567,7 @@ static struct platform_driver ingenic_nand_driver = { .remove = ingenic_nand_remove, .driver = { .name = DRV_NAME, - .of_match_table = of_match_ptr(ingenic_nand_dt_match), + .of_match_table = ingenic_nand_dt_match, }, }; module_platform_driver(ingenic_nand_driver); diff --git a/drivers/mtd/nand/raw/ingenic/jz4780_bch.c b/drivers/mtd/nand/raw/ingenic/jz4780_bch.c index d67dbfff76cc..12b5b0484fe9 100644 --- a/drivers/mtd/nand/raw/ingenic/jz4780_bch.c +++ b/drivers/mtd/nand/raw/ingenic/jz4780_bch.c @@ -260,7 +260,7 @@ static struct platform_driver jz4780_bch_driver = { .probe = jz4780_bch_probe, .driver = { .name = "jz4780-bch", - .of_match_table = of_match_ptr(jz4780_bch_dt_match), + .of_match_table = jz4780_bch_dt_match, }, }; module_platform_driver(jz4780_bch_driver); diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c index 1b47964cb6da..e7df3dac705e 100644 --- a/drivers/mtd/nand/raw/mtk_ecc.c +++ b/drivers/mtd/nand/raw/mtk_ecc.c @@ -579,7 +579,7 @@ static struct platform_driver mtk_ecc_driver = { .probe = mtk_ecc_probe, .driver = { .name = "mtk-ecc", - .of_match_table = of_match_ptr(mtk_ecc_dt_match), + .of_match_table = mtk_ecc_dt_match, #ifdef CONFIG_PM_SLEEP .pm = &mtk_ecc_pm_ops, #endif diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index e7b2ba016d8c..284fff62ac49 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -321,7 +321,7 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs) if (nand_region_is_secured(chip, ofs, mtd->erasesize)) return -EIO; - if (WARN_ONCE(mtd_expert_analysis_mode, mtd_expert_analysis_warning)) + if (mtd_check_expert_analysis_mode()) return 0; if (chip->legacy.block_bad) @@ -338,16 +338,19 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs) * * Return: -EBUSY if the chip has been suspended, 0 otherwise */ -static int nand_get_device(struct nand_chip *chip) +static void nand_get_device(struct nand_chip *chip) { - mutex_lock(&chip->lock); - if (chip->suspended) { + /* Wait until the device is resumed. */ + while (1) { + mutex_lock(&chip->lock); + if (!chip->suspended) { + mutex_lock(&chip->controller->lock); + return; + } mutex_unlock(&chip->lock); - return -EBUSY; - } - mutex_lock(&chip->controller->lock); - return 0; + wait_event(chip->resume_wq, !chip->suspended); + } } /** @@ -576,9 +579,7 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs) nand_erase_nand(chip, &einfo, 0); /* Write bad block marker to OOB */ - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); ret = nand_markbad_bbm(chip, ofs); nand_release_device(chip); @@ -3826,9 +3827,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, ops->mode != MTD_OPS_RAW) return -ENOTSUPP; - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); if (!ops->datbuf) ret = nand_do_read_oob(chip, from, ops); @@ -4415,13 +4414,11 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd_to_nand(mtd); - int ret; + int ret = 0; ops->retlen = 0; - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -4481,9 +4478,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, return -EIO; /* Grab the lock and see if the device is available */ - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); /* Shift to get first page */ page = (int)(instr->addr >> chip->page_shift); @@ -4570,7 +4565,7 @@ static void nand_sync(struct mtd_info *mtd) pr_debug("%s: called\n", __func__); /* Grab the lock and see if the device is available */ - WARN_ON(nand_get_device(chip)); + nand_get_device(chip); /* Release it and go back */ nand_release_device(chip); } @@ -4587,9 +4582,7 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) int ret; /* Select the NAND device */ - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); nand_select_target(chip, chipnr); @@ -4660,6 +4653,8 @@ static void nand_resume(struct mtd_info *mtd) __func__); } mutex_unlock(&chip->lock); + + wake_up_all(&chip->resume_wq); } /** @@ -5274,25 +5269,24 @@ static void of_get_nand_ecc_legacy_user_config(struct nand_chip *chip) user_conf->placement = of_get_rawnand_ecc_placement_legacy(dn); } -static int of_get_nand_bus_width(struct device_node *np) +static int of_get_nand_bus_width(struct nand_chip *chip) { + struct device_node *dn = nand_get_flash_node(chip); u32 val; + int ret; - if (of_property_read_u32(np, "nand-bus-width", &val)) - return 8; - - switch (val) { - case 8: - case 16: - return val; - default: - return -EIO; - } -} + ret = of_property_read_u32(dn, "nand-bus-width", &val); + if (ret == -EINVAL) + /* Buswidth defaults to 8 if the property does not exist .*/ + return 0; + else if (ret) + return ret; -static bool of_get_nand_on_flash_bbt(struct device_node *np) -{ - return of_property_read_bool(np, "nand-on-flash-bbt"); + if (val == 16) + chip->options |= NAND_BUSWIDTH_16; + else if (val != 8) + return -EINVAL; + return 0; } static int of_get_nand_secure_regions(struct nand_chip *chip) @@ -5368,17 +5362,19 @@ static int rawnand_dt_init(struct nand_chip *chip) { struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(chip)); struct device_node *dn = nand_get_flash_node(chip); + int ret; if (!dn) return 0; - if (of_get_nand_bus_width(dn) == 16) - chip->options |= NAND_BUSWIDTH_16; + ret = of_get_nand_bus_width(chip); + if (ret) + return ret; if (of_property_read_bool(dn, "nand-is-boot-medium")) chip->options |= NAND_IS_BOOT_MEDIUM; - if (of_get_nand_on_flash_bbt(dn)) + if (of_property_read_bool(dn, "nand-on-flash-bbt")) chip->bbt_options |= NAND_BBT_USE_FLASH; of_get_nand_ecc_user_config(nand); @@ -5437,6 +5433,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, chip->cur_cs = -1; mutex_init(&chip->lock); + init_waitqueue_head(&chip->resume_wq); /* Enforce the right timings for reset/detection */ chip->current_interface_config = nand_get_reset_interface_config(); diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c index ab630af3a309..a3723da2e0a0 100644 --- a/drivers/mtd/nand/raw/nand_bbt.c +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -1455,7 +1455,7 @@ int nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt) pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", (unsigned int)offs, block, res); - if (WARN_ONCE(mtd_expert_analysis_mode, mtd_expert_analysis_warning)) + if (mtd_check_expert_analysis_mode()) return 0; switch (res) { diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c index 0750121ac371..24beade95c7f 100644 --- a/drivers/mtd/nand/raw/nandsim.c +++ b/drivers/mtd/nand/raw/nandsim.c @@ -201,6 +201,9 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " /* Calculate the OOB offset in flash RAM image by (row, column) address */ #define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) +/* Calculate the byte shift in the next page to access */ +#define NS_PAGE_BYTE_SHIFT(ns) ((ns)->regs.column + (ns)->regs.off) + /* After a command is input, the simulator goes to one of the following states */ #define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */ #define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */ @@ -979,15 +982,8 @@ static int ns_read_error(unsigned int page_no) static int ns_setup_wear_reporting(struct mtd_info *mtd) { - size_t mem; - wear_eb_count = div_u64(mtd->size, mtd->erasesize); - mem = wear_eb_count * sizeof(unsigned long); - if (mem / sizeof(unsigned long) != wear_eb_count) { - NS_ERR("Too many erase blocks for wear reporting\n"); - return -ENOMEM; - } - erase_block_wear = kzalloc(mem, GFP_KERNEL); + erase_block_wear = kcalloc(wear_eb_count, sizeof(unsigned long), GFP_KERNEL); if (!erase_block_wear) { NS_ERR("Too many erase blocks for wear reporting\n"); return -ENOMEM; @@ -1389,7 +1385,7 @@ static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns) */ static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) { - return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; + return NS_GET_PAGE(ns)->byte + NS_PAGE_BYTE_SHIFT(ns); } static int ns_do_read_error(struct nandsim *ns, int num) @@ -1415,7 +1411,7 @@ static void ns_do_bit_flips(struct nandsim *ns, int num) ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); NS_WARN("read_page: flipping bit %d in page %d " "reading from %d ecc: corrected=%u failed=%u\n", - pos, ns->regs.row, ns->regs.column + ns->regs.off, + pos, ns->regs.row, NS_PAGE_BYTE_SHIFT(ns), nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); } } @@ -1437,7 +1433,7 @@ static void ns_read_page(struct nandsim *ns, int num) ssize_t tx; NS_DBG("read_page: page %d written, reading from %d\n", - ns->regs.row, ns->regs.column + ns->regs.off); + ns->regs.row, NS_PAGE_BYTE_SHIFT(ns)); if (ns_do_read_error(ns, num)) return; pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; @@ -1458,7 +1454,7 @@ static void ns_read_page(struct nandsim *ns, int num) memset(ns->buf.byte, 0xFF, num); } else { NS_DBG("read_page: page %d allocated, reading from %d\n", - ns->regs.row, ns->regs.column + ns->regs.off); + ns->regs.row, NS_PAGE_BYTE_SHIFT(ns)); if (ns_do_read_error(ns, num)) return; memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); @@ -1509,7 +1505,7 @@ static int ns_prog_page(struct nandsim *ns, int num) int all; NS_DBG("prog_page: writing page %d\n", ns->regs.row); - pg_off = ns->file_buf + ns->regs.column + ns->regs.off; + pg_off = ns->file_buf + NS_PAGE_BYTE_SHIFT(ns); off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; if (!test_bit(ns->regs.row, ns->pages_written)) { all = 1; @@ -1598,7 +1594,7 @@ static int ns_do_state_action(struct nandsim *ns, uint32_t action) NS_ERR("do_state_action: column number is too large\n"); break; } - num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + num = ns->geom.pgszoob - NS_PAGE_BYTE_SHIFT(ns); ns_read_page(ns, num); NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", @@ -1666,7 +1662,7 @@ static int ns_do_state_action(struct nandsim *ns, uint32_t action) return -1; } - num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + num = ns->geom.pgszoob - NS_PAGE_BYTE_SHIFT(ns); if (num != ns->regs.count) { NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n", ns->regs.count, num); @@ -1738,14 +1734,6 @@ static void ns_switch_state(struct nandsim *ns) "state: %s, nxstate: %s\n", ns_get_state_name(ns->state), ns_get_state_name(ns->nxstate)); - - /* See, whether we need to do some action */ - if ((ns->state & ACTION_MASK) && - ns_do_state_action(ns, ns->state) < 0) { - ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } - } else { /* * We don't yet know which operation we perform. @@ -1762,12 +1750,13 @@ static void ns_switch_state(struct nandsim *ns) if (ns_find_operation(ns, 0)) return; + } - if ((ns->state & ACTION_MASK) && - ns_do_state_action(ns, ns->state) < 0) { - ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); - return; - } + /* See, whether we need to do some action */ + if ((ns->state & ACTION_MASK) && + ns_do_state_action(ns, ns->state) < 0) { + ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; } /* For 16x devices column means the page offset in words */ @@ -1817,7 +1806,7 @@ static void ns_switch_state(struct nandsim *ns) switch (NS_STATE(ns->state)) { case STATE_DATAIN: case STATE_DATAOUT: - ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + ns->regs.num = ns->geom.pgszoob - NS_PAGE_BYTE_SHIFT(ns); break; case STATE_DATAOUT_ID: diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index f0bbbe401e76..58c32a11792e 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -2298,7 +2298,7 @@ static struct platform_driver omap_nand_driver = { .remove = omap_nand_remove, .driver = { .name = DRIVER_NAME, - .of_match_table = of_match_ptr(omap_nand_ids), + .of_match_table = omap_nand_ids, }, }; diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c index db105d9b560c..893e9979c4a2 100644 --- a/drivers/mtd/nand/raw/omap_elm.c +++ b/drivers/mtd/nand/raw/omap_elm.c @@ -282,7 +282,7 @@ static void elm_start_processing(struct elm_info *info, static void elm_error_correction(struct elm_info *info, struct elm_errorvec *err_vec) { - int i, j, errors = 0; + int i, j; int offset; u32 reg_val; @@ -312,8 +312,6 @@ static void elm_error_correction(struct elm_info *info, /* Update error location register */ offset += 4; } - - errors += err_vec[i].error_count; } else { err_vec[i].error_uncorrectable = true; } diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index 8a91e069ee2e..3c6f6aff649f 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -1062,7 +1062,7 @@ static int pl35x_nand_chip_init(struct pl35x_nandc *nfc, chip->controller = &nfc->controller; mtd = nand_to_mtd(chip); mtd->dev.parent = nfc->dev; - nand_set_flash_node(chip, nfc->dev->of_node); + nand_set_flash_node(chip, np); if (!mtd->name) { mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL, "%s", PL35X_NANDC_DRIVER_NAME); diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c index 428e08362956..6db063b230a9 100644 --- a/drivers/mtd/nand/raw/renesas-nand-controller.c +++ b/drivers/mtd/nand/raw/renesas-nand-controller.c @@ -1412,7 +1412,7 @@ MODULE_DEVICE_TABLE(of, rnandc_id_table); static struct platform_driver rnandc_driver = { .driver = { .name = "renesas-nandc", - .of_match_table = of_match_ptr(rnandc_id_table), + .of_match_table = rnandc_id_table, }, .probe = rnandc_probe, .remove = rnandc_remove, diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c index b5405bc7ca3a..cbaa4f1c83da 100644 --- a/drivers/mtd/nand/raw/rockchip-nand-controller.c +++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c @@ -1403,7 +1403,6 @@ static int rk_nfc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(dev, "no NFC irq resource\n"); ret = -EINVAL; goto clk_disable; } diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index 13df4bdf792a..b85b9c6fcc42 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -1220,7 +1220,7 @@ static struct platform_driver flctl_driver = { .remove = flctl_remove, .driver = { .name = "sh_flctl", - .of_match_table = of_match_ptr(of_flctl_match), + .of_match_table = of_flctl_match, }, }; diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 97b4e02e43e4..87c1c7dd97eb 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -9,6 +9,7 @@ #include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <linux/errno.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/iopoll.h> #include <linux/mfd/syscon.h> @@ -231,6 +232,7 @@ struct stm32_fmc2_timings { struct stm32_fmc2_nand { struct nand_chip chip; + struct gpio_desc *wp_gpio; struct stm32_fmc2_timings timings; int ncs; int cs_used[FMC2_MAX_CE]; @@ -1747,6 +1749,18 @@ static const struct nand_controller_ops stm32_fmc2_nfc_controller_ops = { .setup_interface = stm32_fmc2_nfc_setup_interface, }; +static void stm32_fmc2_nfc_wp_enable(struct stm32_fmc2_nand *nand) +{ + if (nand->wp_gpio) + gpiod_set_value(nand->wp_gpio, 1); +} + +static void stm32_fmc2_nfc_wp_disable(struct stm32_fmc2_nand *nand) +{ + if (nand->wp_gpio) + gpiod_set_value(nand->wp_gpio, 0); +} + static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc, struct device_node *dn) { @@ -1785,6 +1799,18 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc, nand->cs_used[i] = cs; } + nand->wp_gpio = devm_gpiod_get_from_of_node(nfc->dev, dn, + "wp-gpios", 0, + GPIOD_OUT_HIGH, "wp"); + if (IS_ERR(nand->wp_gpio)) { + ret = PTR_ERR(nand->wp_gpio); + if (ret != -ENOENT) + return dev_err_probe(nfc->dev, ret, + "failed to request WP GPIO\n"); + + nand->wp_gpio = NULL; + } + nand_set_flash_node(&nand->chip, dn); return 0; @@ -1956,10 +1982,12 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev) chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA; + stm32_fmc2_nfc_wp_disable(nand); + /* Scan to find existence of the device */ ret = nand_scan(chip, nand->ncs); if (ret) - goto err_release_dma; + goto err_wp_enable; ret = mtd_device_register(mtd, NULL, 0); if (ret) @@ -1972,6 +2000,9 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev) err_nand_cleanup: nand_cleanup(chip); +err_wp_enable: + stm32_fmc2_nfc_wp_enable(nand); + err_release_dma: if (nfc->dma_ecc_ch) dma_release_channel(nfc->dma_ecc_ch); @@ -2012,15 +2043,20 @@ static int stm32_fmc2_nfc_remove(struct platform_device *pdev) clk_disable_unprepare(nfc->clk); + stm32_fmc2_nfc_wp_enable(nand); + return 0; } static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev) { struct stm32_fmc2_nfc *nfc = dev_get_drvdata(dev); + struct stm32_fmc2_nand *nand = &nfc->nand; clk_disable_unprepare(nfc->clk); + stm32_fmc2_nfc_wp_enable(nand); + pinctrl_pm_select_sleep_state(dev); return 0; @@ -2042,6 +2078,8 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev) stm32_fmc2_nfc_init(nfc); + stm32_fmc2_nfc_wp_disable(nand); + for (chip_cs = 0; chip_cs < FMC2_MAX_CE; chip_cs++) { if (!(nfc->cs_assigned & BIT(chip_cs))) continue; |