diff options
| author | Miquel Raynal <miquel.raynal@bootlin.com> | 2026-10-23 18:49:30 +0200 |
|---|---|---|
| committer | Miquel Raynal <miquel.raynal@bootlin.com> | 2026-10-23 18:49:30 +0200 |
| commit | 76ebe529a48518d0fa671091a8c2f1a01069aa49 (patch) | |
| tree | 26dd81a02d188de286ee5e25d17194f30fb8d9b1 | |
| parent | 2f7fa8d90694d21dce68ceabd8383252b111cfea (diff) | |
| parent | 30daaf3e7897b4a8d9b72e5b0c86dd6e2a4d7d85 (diff) | |
Merge tag 'nand/for-7.4' into mtd/next
Raw NAND:
- The Qualcomm controller driver has been cleaned up again.
- Arasan and IBM have been improved/created.
- Reflect that ESMT and GigaDevice share ID 0xc8, and disable broken
timings feature on GD9FU2G8F3A.
- Platform data support has been dropped from the lpc32xx drivers.
SPI NAND:
- Heyangtek uses two bytes for the BBM like every other device.
- Winbond: Add support for W25N04LW and W25N08LW.
- Support for ISSI SPI NAND flashes.
Then there is the load of API updates and misc AI fixes.
31 files changed, 690 insertions, 368 deletions
diff --git a/Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml b/Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml index b90d3b48c2f2..7154245b00e3 100644 --- a/Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml +++ b/Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml @@ -35,6 +35,9 @@ properties: interrupts: maxItems: 1 + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/mtd/ibm,ndfc.txt b/Documentation/devicetree/bindings/mtd/ibm,ndfc.txt deleted file mode 100644 index 869f0b5f16e8..000000000000 --- a/Documentation/devicetree/bindings/mtd/ibm,ndfc.txt +++ /dev/null @@ -1,39 +0,0 @@ -AMCC NDFC (NanD Flash Controller) - -Required properties: -- compatible : "ibm,ndfc". -- reg : should specify chip select and size used for the chip (0x2000). - -Optional properties: -- ccr : NDFC config and control register value (default 0). -- bank-settings : NDFC bank configuration register value (default 0). - -Notes: -- partition(s) - follows the OF MTD standard for partitions - -Example: - -ndfc@1,0 { - compatible = "ibm,ndfc"; - reg = <0x00000001 0x00000000 0x00002000>; - ccr = <0x00001000>; - bank-settings = <0x80002222>; - #address-cells = <1>; - #size-cells = <1>; - - nand { - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "kernel"; - reg = <0x00000000 0x00200000>; - }; - partition@200000 { - label = "root"; - reg = <0x00200000 0x03E00000>; - }; - }; -}; - - diff --git a/Documentation/devicetree/bindings/mtd/ibm,ndfc.yaml b/Documentation/devicetree/bindings/mtd/ibm,ndfc.yaml new file mode 100644 index 000000000000..d5c392001ad3 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/ibm,ndfc.yaml @@ -0,0 +1,128 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/ibm,ndfc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: IBM/AMCC NDFC (NAND Flash Controller) + +maintainers: + - Miquel Raynal <miquel.raynal@bootlin.com> + - Richard Weinberger <richard@nod.at> + +description: | + The NDFC is the NAND flash controller integrated into the IBM/AMCC + PowerPC 44x SoCs. + +allOf: + - $ref: nand-controller-legacy.yaml# + +properties: + compatible: + const: ibm,ndfc + + reg: + description: Chip select and size used for the chip. + maxItems: 1 + + ccr: + description: NDFC config and control register value. + $ref: /schemas/types.yaml#/definitions/uint32 + + bank-settings: + description: NDFC bank configuration register value. + $ref: /schemas/types.yaml#/definitions/uint32 + + nand: + type: object + description: | + NAND chip attached to the controller. The driver picks the first + child of the controller node, so this node is not required to be + named "nand@<cs>". + + allOf: + - $ref: mtd.yaml# + - $ref: nand-property.yaml# + - $ref: raw-nand-property.yaml# + + properties: + "#address-cells": + const: 1 + "#size-cells": + const: 1 + + patternProperties: + "^partition@[0-9a-f]+$": + $ref: /schemas/mtd/partitions/partition.yaml#/$defs/partition-node + deprecated: true + + unevaluatedProperties: false + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + ebc { + #address-cells = <2>; + #size-cells = <1>; + + ndfc@1,0 { + compatible = "ibm,ndfc"; + reg = <0x00000001 0x00000000 0x00002000>; + ccr = <0x00001000>; + bank-settings = <0x80002222>; + #address-cells = <1>; + #size-cells = <1>; + + nand { + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "kernel"; + reg = <0x00000000 0x00200000>; + }; + partition@200000 { + label = "root"; + reg = <0x00200000 0x03e00000>; + }; + }; + }; + }; + - | + ebc { + #address-cells = <2>; + #size-cells = <1>; + + ndfc@1,0 { + compatible = "ibm,ndfc"; + reg = <0x00000001 0x00000000 0x00002000>; + #address-cells = <1>; + #size-cells = <1>; + + nand { + #address-cells = <1>; + #size-cells = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "kernel"; + reg = <0x00000000 0x00200000>; + }; + partition@200000 { + label = "root"; + reg = <0x00200000 0x03e00000>; + }; + }; + }; + }; + }; +... diff --git a/drivers/mtd/nand/onenand/onenand_omap2.c b/drivers/mtd/nand/onenand/onenand_omap2.c index 0793251ada3b..644498187d8f 100644 --- a/drivers/mtd/nand/onenand/onenand_omap2.c +++ b/drivers/mtd/nand/onenand/onenand_omap2.c @@ -340,9 +340,9 @@ static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c, tx->callback = omap2_onenand_dma_complete_func; tx->callback_param = &c->dma_done; - cookie = tx->tx_submit(tx); + cookie = dmaengine_submit(tx); if (dma_submit_error(cookie)) { - dev_err(&c->pdev->dev, "Failed to do DMA tx_submit\n"); + dev_err(&c->pdev->dev, "Failed to do dmaengine_submit\n"); return -EIO; } diff --git a/drivers/mtd/nand/qpic_common.c b/drivers/mtd/nand/qpic_common.c index 4f3e4dd766da..91ae970734f8 100644 --- a/drivers/mtd/nand/qpic_common.c +++ b/drivers/mtd/nand/qpic_common.c @@ -775,5 +775,136 @@ unalloc: } EXPORT_SYMBOL(qcom_nandc_alloc); +/* reads back FLASH_STATUS register set by the controller */ +int qcom_nandc_check_flash_errors(struct qcom_nand_controller *nandc, + int cw_cnt) +{ + int i; + + qcom_nandc_dev_to_mem(nandc, true); + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); + + if (flash & (FS_OP_ERR | FS_MPU_ERR)) + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(qcom_nandc_check_flash_errors); + +/* + * Helper to prepare DMA descriptors for configuring registers + * before reading a NAND page. + */ +void qcom_nandc_config_page_read(struct qcom_nand_controller *nandc) +{ + qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); + qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); + if (!nandc->props->qpic_version2) + qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); + qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, + NAND_ERASED_CW_DETECT_CFG, 1, 0); + qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, + NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); +} +EXPORT_SYMBOL(qcom_nandc_config_page_read); + +/* + * Helper to prepare DMA descriptors used to configure registers needed for + * before writing a NAND page. + */ +void qcom_nandc_config_page_write(struct qcom_nand_controller *nandc) +{ + qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); + qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); + if (!nandc->props->qpic_version2) + qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, + NAND_BAM_NEXT_SGL); +} +EXPORT_SYMBOL(qcom_nandc_config_page_write); + +/* + * Helper to prepare DMA descriptors for configuring registers + * before writing each codeword in NAND page. + */ +void qcom_nandc_config_cw_write(struct qcom_nand_controller *nandc) +{ + qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); + qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, + NAND_BAM_NEXT_SGL); +} +EXPORT_SYMBOL(qcom_nandc_config_cw_write); + +/** + * qcom_nandc_set_read_loc_first() - to set read location first register + * @nandc: NAND controller's private data + * @reg_base: location register base + * @cw_offset: code word offset + * @read_size: code word read length + * @is_last_read_loc: is this the last read location + * + * This function will set location register value + */ +void qcom_nandc_set_read_loc_first(struct qcom_nand_controller *nandc, + int reg_base, u32 cw_offset, + u32 read_size, u32 is_last_read_loc) +{ + __le32 locreg_val; + u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | + FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | + FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); + + locreg_val = cpu_to_le32(val); + + if (reg_base == NAND_READ_LOCATION_0) + nandc->regs->read_location0 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_1) + nandc->regs->read_location1 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_2) + nandc->regs->read_location2 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_3) + nandc->regs->read_location3 = locreg_val; +} +EXPORT_SYMBOL(qcom_nandc_set_read_loc_first); + +/** + * qcom_nandc_set_read_loc_last - to set read location last register + * @nandc: NAND controller's private data + * @reg_base: location register base + * @cw_offset: code word offset + * @read_size: code word read length + * @is_last_read_loc: is this the last read location + * + * This function will set location last register value + */ +void qcom_nandc_set_read_loc_last(struct qcom_nand_controller *nandc, + int reg_base, u32 cw_offset, + u32 read_size, u32 is_last_read_loc) +{ + __le32 locreg_val; + u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | + FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | + FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); + + locreg_val = cpu_to_le32(val); + + if (reg_base == NAND_READ_LOCATION_LAST_CW_0) + nandc->regs->read_location_last0 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_LAST_CW_1) + nandc->regs->read_location_last1 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_LAST_CW_2) + nandc->regs->read_location_last2 = locreg_val; + else if (reg_base == NAND_READ_LOCATION_LAST_CW_3) + nandc->regs->read_location_last3 = locreg_val; +} +EXPORT_SYMBOL(qcom_nandc_set_read_loc_last); + MODULE_DESCRIPTION("QPIC controller common api"); MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 8a19408abb63..0bdbcbb50d94 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -2085,7 +2085,7 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, if (!np) { dev_err(dev, "Missing or invalid atmel,smc property\n"); ret = -EINVAL; - goto out_release_dma; + goto out_put_mck; } nc->smc = syscon_node_to_regmap(np); @@ -2093,11 +2093,13 @@ 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); - goto out_release_dma; + goto out_put_mck; } return 0; +out_put_mck: + clk_put(nc->mck); out_release_dma: if (nc->dmac) dma_release_channel(nc->dmac); diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 5b9dadd5405e..4a1f5e736a1c 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -3272,6 +3272,8 @@ static int brcmnand_edu_setup(struct platform_device *pdev) ctrl->edu_irq = platform_get_irq_optional(pdev, 1); if (ctrl->edu_irq < 0) { + if (ctrl->edu_irq != -ENXIO) + return ctrl->edu_irq; dev_warn(dev, "FLASH EDU enabled, using ctlrdy irq\n"); } else { diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index a88ac2cfaccd..cc75b6a67e07 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -12,7 +12,6 @@ #include <linux/platform_device.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/of_address.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> #include <linux/mtd/rawnand.h> @@ -50,7 +49,6 @@ struct fsl_ifc_nand_ctrl { unsigned int index; /* Pointer to next byte to 'read' */ unsigned int oob; /* Non zero if operating on OOB data */ unsigned int eccread; /* Non zero for a full-page ECC read */ - unsigned int counter; /* counter for the initializations */ unsigned int max_bitflips; /* Saved during READ0 cmd */ }; @@ -965,13 +963,6 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) { - struct mtd_info *mtd = nand_to_mtd(&priv->chip); - - kfree(mtd->name); - - if (priv->vbase) - iounmap(priv->vbase); - ifc_nand_ctrl->chips[priv->bank] = NULL; return 0; @@ -996,12 +987,12 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) { struct fsl_ifc_runtime __iomem *ifc; struct fsl_ifc_mtd *priv; - struct resource res; + struct resource *res; + void __iomem *vbase; static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", "ofpart", NULL }; int ret; int bank; - struct device_node *node = dev->dev.of_node; struct mtd_info *mtd; if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) @@ -1009,15 +1000,13 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) ifc = fsl_ifc_ctrl_dev->rregs; /* get, allocate and map the memory resource */ - ret = of_address_to_resource(node, 0, &res); - if (ret) { - dev_err(&dev->dev, "%s: failed to get resource\n", __func__); - return ret; - } + vbase = devm_platform_get_and_ioremap_resource(dev, 0, &res); + if (IS_ERR(vbase)) + return PTR_ERR(vbase); /* find which chip select it is connected to */ for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { - if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) + if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res->start)) break; } @@ -1033,15 +1022,13 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) mutex_lock(&fsl_ifc_nand_mutex); if (!fsl_ifc_ctrl_dev->nand) { - ifc_nand_ctrl = kzalloc_obj(*ifc_nand_ctrl); + ifc_nand_ctrl = devm_kzalloc(fsl_ifc_ctrl_dev->dev, sizeof(*ifc_nand_ctrl), + GFP_KERNEL); if (!ifc_nand_ctrl) { mutex_unlock(&fsl_ifc_nand_mutex); return -ENOMEM; } - ifc_nand_ctrl->read_bytes = 0; - ifc_nand_ctrl->index = 0; - ifc_nand_ctrl->addr = NULL; fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; nand_controller_init(&ifc_nand_ctrl->controller); @@ -1054,13 +1041,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) priv->bank = bank; priv->ctrl = fsl_ifc_ctrl_dev; priv->dev = &dev->dev; - - priv->vbase = ioremap(res.start, resource_size(&res)); - if (!priv->vbase) { - dev_err(priv->dev, "%s: failed to map chip region\n", __func__); - ret = -ENOMEM; - goto err; - } + priv->vbase = vbase; dev_set_drvdata(priv->dev, priv); @@ -1076,7 +1057,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) &ifc->ifc_nand.nand_evter_intr_en); mtd = nand_to_mtd(&priv->chip); - mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); + mtd->name = devm_kasprintf(priv->dev, GFP_KERNEL, "%llx.flash", (u64)res->start); if (!mtd->name) { ret = -ENOMEM; goto err; @@ -1098,7 +1079,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) goto cleanup_nand; dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", - (unsigned long long)res.start, priv->bank); + (unsigned long long)res->start, priv->bank); return 0; @@ -1121,14 +1102,6 @@ static void fsl_ifc_nand_remove(struct platform_device *dev) nand_cleanup(chip); fsl_ifc_chip_remove(priv); - - mutex_lock(&fsl_ifc_nand_mutex); - ifc_nand_ctrl->counter--; - if (!ifc_nand_ctrl->counter) { - fsl_ifc_ctrl_dev->nand = NULL; - kfree(ifc_nand_ctrl); - } - mutex_unlock(&fsl_ifc_nand_mutex); } static const struct of_device_id fsl_ifc_nand_match[] = { diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 08ef4425559c..837c23c63aeb 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -524,7 +524,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, tx->callback = dma_complete; tx->callback_param = host; - cookie = tx->tx_submit(tx); + cookie = dmaengine_submit(tx); ret = dma_submit_error(cookie); if (ret) { diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c index 0a4f5ff11133..94d2161bf5a1 100644 --- a/drivers/mtd/nand/raw/intel-nand-controller.c +++ b/drivers/mtd/nand/raw/intel-nand-controller.c @@ -324,7 +324,7 @@ static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir, tx->callback = callback; tx->callback_param = ebu_host; - cookie = tx->tx_submit(tx); + cookie = dmaengine_submit(tx); ret = dma_submit_error(cookie); if (ret) { diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index b7162ced9efa..b8b29341b88c 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -19,6 +19,7 @@ #define NAND_MFR_AMD 0x01 #define NAND_MFR_ATO 0x9b #define NAND_MFR_EON 0x92 +/* ESMT and GigaDevice share the same manufacturer ID. */ #define NAND_MFR_ESMT 0xc8 #define NAND_MFR_FUJITSU 0x04 #define NAND_MFR_HYNIX 0xad diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 4400989df8a0..d03486dc4115 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -26,7 +26,6 @@ #include <linux/interrupt.h> #include <linux/of.h> #include <linux/gpio/consumer.h> -#include <linux/mtd/lpc32xx_mlc.h> #include <linux/io.h> #include <linux/mm.h> #include <linux/dma-mapping.h> @@ -174,7 +173,6 @@ static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = { struct lpc32xx_nand_host { struct platform_device *pdev; struct nand_chip nand_chip; - struct lpc32xx_mlc_platform_data *pdata; struct clk *clk; struct gpio_desc *wp_gpio; void __iomem *io_base; @@ -578,24 +576,11 @@ static void lpc32xx_ecc_enable(struct nand_chip *chip, int mode) static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host) { struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); - dma_cap_mask_t mask; host->dma_chan = dma_request_chan(mtd->dev.parent, "rx-tx"); if (IS_ERR(host->dma_chan)) { - /* fallback to request using platform data */ - if (!host->pdata || !host->pdata->dma_filter) { - dev_err(mtd->dev.parent, "no DMA platform data\n"); - return -ENOENT; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, "nand-mlc"); - - if (!host->dma_chan) { - dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); - return -EBUSY; - } + dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); + return PTR_ERR(host->dma_chan); } /* @@ -734,8 +719,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) gpiod_set_consumer_name(host->wp_gpio, "NAND WP"); - host->pdata = dev_get_platdata(&pdev->dev); - /* link the private data structures */ nand_set_controller_data(nand_chip, host); nand_set_flash_node(nand_chip, pdev->dev.of_node); diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index bdc3957369c2..638267d44360 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -25,7 +25,6 @@ #include <linux/dmaengine.h> #include <linux/gpio/consumer.h> #include <linux/of.h> -#include <linux/mtd/lpc32xx_slc.h> #define LPC32XX_MODNAME "lpc32xx-nand" @@ -213,7 +212,6 @@ struct lpc32xx_nand_cfg_slc { struct lpc32xx_nand_host { struct nand_chip nand_chip; - struct lpc32xx_slc_platform_data *pdata; struct clk *clk; struct gpio_desc *wp_gpio; void __iomem *io_base; @@ -725,24 +723,11 @@ static int lpc32xx_nand_write_page_raw_syndrome(struct nand_chip *chip, static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) { struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); - dma_cap_mask_t mask; host->dma_chan = dma_request_chan(mtd->dev.parent, "rx-tx"); if (IS_ERR(host->dma_chan)) { - /* fallback to request using platform data */ - if (!host->pdata || !host->pdata->dma_filter) { - dev_err(mtd->dev.parent, "no DMA platform data\n"); - return -ENOENT; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, "nand-slc"); - - if (!host->dma_chan) { - dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); - return -EBUSY; - } + dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); + return PTR_ERR(host->dma_chan); } return 0; @@ -871,8 +856,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) gpiod_set_consumer_name(host->wp_gpio, "NAND WP"); - host->pdata = dev_get_platdata(&pdev->dev); - chip = &host->nand_chip; mtd = nand_to_mtd(chip); nand_set_controller_data(chip, host); diff --git a/drivers/mtd/nand/raw/nand_esmt.c b/drivers/mtd/nand/raw/nand_esmt.c index 4412c407aef3..117b110fc54a 100644 --- a/drivers/mtd/nand/raw/nand_esmt.c +++ b/drivers/mtd/nand/raw/nand_esmt.c @@ -3,6 +3,9 @@ * Copyright (C) 2018 Toradex AG * * Author: Marcel Ziswiler <marcel.ziswiler@toradex.com> + * + * ESMT and GigaDevice share the same JEDEC manufacturer ID (0xc8), so this + * file handles chips from both vendors. */ #include <linux/mtd/rawnand.h> @@ -38,6 +41,35 @@ static void esmt_nand_decode_id(struct nand_chip *chip) nanddev_set_ecc_requirements(base, &requirements); } +/* + * GigaDevice GD9FU2G8F3A does not implement the timing mode feature: + * GET_FEATURES does not report the mode that was written, and + * SET_FEATURES is ignored, even though the parameter page advertises + * support. The vendor confirmed the chip works at every advertised + * timing mode without being told, so unflag the feature and configure + * the host side only. + */ +static void esmt_nand_fix_broken_get_timings(struct nand_chip *chip) +{ + int i; + static const char * const broken_get_timings[] = { + "GD9FU2G8F3A", + }; + + if (!chip->parameters.supports_set_get_features) + return; + + i = match_string(broken_get_timings, ARRAY_SIZE(broken_get_timings), + chip->parameters.model); + if (i < 0) + return; + + bitmap_clear(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + bitmap_clear(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); +} + static int esmt_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) @@ -50,6 +82,8 @@ static int esmt_nand_init(struct nand_chip *chip) chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE | NAND_BBM_LASTPAGE; + esmt_nand_fix_broken_get_timings(chip); + return 0; } diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c index 08707ce9d7c0..9b2dd246398f 100644 --- a/drivers/mtd/nand/raw/nand_ids.c +++ b/drivers/mtd/nand/raw/nand_ids.c @@ -185,7 +185,7 @@ static const struct nand_manufacturer_desc nand_manufacturer_descs[] = { {NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops}, {NAND_MFR_ATO, "ATO"}, {NAND_MFR_EON, "Eon"}, - {NAND_MFR_ESMT, "ESMT", &esmt_nand_manuf_ops}, + {NAND_MFR_ESMT, "ESMT/GigaDevice", &esmt_nand_manuf_ops}, {NAND_MFR_FUJITSU, "Fujitsu"}, {NAND_MFR_HYNIX, "Hynix", &hynix_nand_manuf_ops}, {NAND_MFR_INTEL, "Intel"}, diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index a48274297d3b..d7b119028a71 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -162,8 +162,8 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, return -ENODEV; nand_set_flash_node(chip, flash_np); - mtd->name = kasprintf(GFP_KERNEL, "%s.%pOFn", dev_name(&ndfc->ofdev->dev), - flash_np); + mtd->name = devm_kasprintf(&ndfc->ofdev->dev, GFP_KERNEL, "%s.%pOFn", + dev_name(&ndfc->ofdev->dev), flash_np); if (!mtd->name) { ret = -ENOMEM; goto err; @@ -177,18 +177,21 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, err: of_node_put(flash_np); - if (ret) - kfree(mtd->name); return ret; } static int ndfc_probe(struct platform_device *ofdev) { struct ndfc_controller *ndfc; + void __iomem *ndfcbase; const __be32 *reg; u32 ccr; u32 cs; - int err, len = 0; + int len = 0; + + ndfcbase = devm_platform_ioremap_resource(ofdev, 0); + if (IS_ERR(ndfcbase)) + return PTR_ERR(ndfcbase); /* Read the reg property to get the chip select */ reg = of_get_property(ofdev->dev.of_node, "reg", &len); @@ -210,11 +213,7 @@ static int ndfc_probe(struct platform_device *ofdev) ndfc->ofdev = ofdev; dev_set_drvdata(&ofdev->dev, ndfc); - ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0); - if (!ndfc->ndfcbase) { - dev_err(&ofdev->dev, "failed to get memory\n"); - return -EIO; - } + ndfc->ndfcbase = ndfcbase; ccr = NDFC_CCR_BS(ndfc->chip_select); @@ -232,13 +231,7 @@ static int ndfc_probe(struct platform_device *ofdev) iowrite32be(be32_to_cpup(reg), ndfc->ndfcbase + offset); } - err = ndfc_chip_init(ndfc, ofdev->dev.of_node); - if (err) { - iounmap(ndfc->ndfcbase); - return err; - } - - return 0; + return ndfc_chip_init(ndfc, ofdev->dev.of_node); } static void ndfc_remove(struct platform_device *ofdev) @@ -251,7 +244,6 @@ static void ndfc_remove(struct platform_device *ofdev) ret = mtd_device_unregister(mtd); WARN_ON(ret); nand_cleanup(chip); - kfree(mtd->name); } static const struct of_device_id ndfc_match[] = { diff --git a/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c b/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c index 1a285cd8fad6..f659d3a6b9ef 100644 --- a/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c +++ b/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c @@ -993,7 +993,6 @@ static int ma35_nand_probe(struct platform_device *pdev) ret = ma35_nand_chips_init(&pdev->dev, nand); if (ret) { dev_err(&pdev->dev, "failed to init NAND chips\n"); - clk_disable(nand->clk); return ret; } diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 9f7079e36663..4fa89aee934e 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -666,9 +666,7 @@ static void gen_true_ecc(u8 *ecc_buf) * If there is no error, %0 is returned. If there is an error but it * was corrected, %1 is returned. Otherwise, %-1 is returned. */ -static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ - u8 *ecc_data2, /* read from register */ - u8 *page_data) +static int omap_compare_ecc(u8 *ecc_data1, u8 *ecc_data2, u8 *page_data) { uint i; u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index bd89aaadd1b2..26b61d856143 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -807,6 +807,7 @@ static int pl35x_nfc_setup_interface(struct nand_chip *chip, int cs, * macro to convert from one to the other. */ period_ns = NSEC_PER_SEC / clk_get_rate(mclk); + clk_put(mclk); /* * PL35X SMC needs one extra read cycle in SDR Mode 5. This is not diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index d7642db2e2df..2d98f79b492d 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -149,70 +149,6 @@ static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) return cw == (ecc->steps - 1); } -/** - * nandc_set_read_loc_first() - to set read location first register - * @chip: NAND Private Flash Chip Data - * @reg_base: location register base - * @cw_offset: code word offset - * @read_size: code word read length - * @is_last_read_loc: is this the last read location - * - * This function will set location register value - */ -static void nandc_set_read_loc_first(struct nand_chip *chip, - int reg_base, u32 cw_offset, - u32 read_size, u32 is_last_read_loc) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - __le32 locreg_val; - u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | - FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | - FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); - - locreg_val = cpu_to_le32(val); - - if (reg_base == NAND_READ_LOCATION_0) - nandc->regs->read_location0 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_1) - nandc->regs->read_location1 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_2) - nandc->regs->read_location2 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_3) - nandc->regs->read_location3 = locreg_val; -} - -/** - * nandc_set_read_loc_last - to set read location last register - * @chip: NAND Private Flash Chip Data - * @reg_base: location register base - * @cw_offset: code word offset - * @read_size: code word read length - * @is_last_read_loc: is this the last read location - * - * This function will set location last register value - */ -static void nandc_set_read_loc_last(struct nand_chip *chip, - int reg_base, u32 cw_offset, - u32 read_size, u32 is_last_read_loc) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - __le32 locreg_val; - u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | - FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | - FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); - - locreg_val = cpu_to_le32(val); - - if (reg_base == NAND_READ_LOCATION_LAST_CW_0) - nandc->regs->read_location_last0 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_LAST_CW_1) - nandc->regs->read_location_last1 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_LAST_CW_2) - nandc->regs->read_location_last2 = locreg_val; - else if (reg_base == NAND_READ_LOCATION_LAST_CW_3) - nandc->regs->read_location_last3 = locreg_val; -} - /* helper to configure location register values */ static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg, u32 cw_offset, u32 read_size, u32 is_last_read_loc) @@ -227,11 +163,13 @@ static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg, reg_base += reg * 4; if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) - return nandc_set_read_loc_last(chip, reg_base, cw_offset, - read_size, is_last_read_loc); + return qcom_nandc_set_read_loc_last(nandc, reg_base, cw_offset, + read_size, + is_last_read_loc); else - return nandc_set_read_loc_first(chip, reg_base, cw_offset, - read_size, is_last_read_loc); + return qcom_nandc_set_read_loc_first(nandc, reg_base, cw_offset, + read_size, + is_last_read_loc); } /* helper to configure address register values */ @@ -303,24 +241,6 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, i /* * Helper to prepare DMA descriptors for configuring registers - * before reading a NAND page. - */ -static void config_nand_page_read(struct nand_chip *chip) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); - qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); - if (!nandc->props->qpic_version2) - qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); - qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, - NAND_ERASED_CW_DETECT_CFG, 1, 0); - qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, - NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); -} - -/* - * Helper to prepare DMA descriptors for configuring registers * before reading each codeword in NAND page. */ static void @@ -328,14 +248,16 @@ config_nand_cw_read(struct nand_chip *chip, bool use_ecc, int cw) { struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; - __le32 *reg = &nandc->regs->read_location0; + int reg_base = NAND_READ_LOCATION_0; - if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) + if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) { reg = &nandc->regs->read_location_last0; + reg_base = NAND_READ_LOCATION_LAST_CW_0; + } if (nandc->props->supports_bam) - qcom_write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); + qcom_write_reg_dma(nandc, reg, reg_base, 4, NAND_BAM_NEXT_SGL); qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); @@ -357,41 +279,10 @@ static void config_nand_single_cw_page_read(struct nand_chip *chip, bool use_ecc, int cw) { - config_nand_page_read(chip); - config_nand_cw_read(chip, use_ecc, cw); -} - -/* - * Helper to prepare DMA descriptors used to configure registers needed for - * before writing a NAND page. - */ -static void config_nand_page_write(struct nand_chip *chip) -{ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); - qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); - if (!nandc->props->qpic_version2) - qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, - NAND_BAM_NEXT_SGL); -} - -/* - * Helper to prepare DMA descriptors for configuring registers - * before writing each codeword in NAND page. - */ -static void config_nand_cw_write(struct nand_chip *chip) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); - qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); - - qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); - qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, - NAND_BAM_NEXT_SGL); + qcom_nandc_config_page_read(nandc); + config_nand_cw_read(chip, use_ecc, cw); } /* @@ -442,31 +333,6 @@ static bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len) return true; } -struct read_stats { - __le32 flash; - __le32 buffer; - __le32 erased_cw; -}; - -/* reads back FLASH_STATUS register set by the controller */ -static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int i; - - qcom_nandc_dev_to_mem(nandc, true); - - for (i = 0; i < cw_cnt; i++) { - u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); - - if (flash & (FS_OP_ERR | FS_MPU_ERR)) - return -EIO; - } - - return 0; -} - /* performs raw read for one codeword */ static int qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, @@ -491,7 +357,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, qcom_clear_bam_transaction(nandc); set_address(host, host->cw_size * cw, page); update_rw_regs(host, 1, true, raw_cw); - config_nand_page_read(chip); + qcom_nandc_config_page_read(nandc); data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); oob_size1 = host->bbm_size; @@ -538,7 +404,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, return ret; } - return check_flash_errors(host, 1); + return qcom_nandc_check_flash_errors(nandc, 1); } /* @@ -624,12 +490,12 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0, uncorrectable_cws = 0; - struct read_stats *buf; + struct qcom_nandc_read_stats *buf; bool flash_op_err = false, erased; int i; u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; - buf = (struct read_stats *)nandc->reg_read_buf; + buf = (struct qcom_nandc_read_stats *)nandc->reg_read_buf; qcom_nandc_dev_to_mem(nandc, true); for (i = 0; i < ecc->steps; i++, buf++) { @@ -728,7 +594,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; int i, ret; - config_nand_page_read(chip); + qcom_nandc_config_page_read(nandc); /* queue cmd descs for each codeword */ for (i = 0; i < ecc->steps; i++) { @@ -990,7 +856,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const u8 *buf, host->use_ecc = true; update_rw_regs(host, ecc->steps, false, 0); - config_nand_page_write(chip); + qcom_nandc_config_page_write(nandc); for (i = 0; i < ecc->steps; i++) { int data_size, oob_size; @@ -1021,7 +887,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const u8 *buf, oob_buf, oob_size, 0); } - config_nand_cw_write(chip); + qcom_nandc_config_cw_write(nandc); data_buf += data_size; oob_buf += oob_size; @@ -1060,7 +926,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, host->use_ecc = false; update_rw_regs(host, ecc->steps, false, 0); - config_nand_page_write(chip); + qcom_nandc_config_page_write(nandc); for (i = 0; i < ecc->steps; i++) { int data_size1, data_size2, oob_size1, oob_size2; @@ -1097,7 +963,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); oob_buf += oob_size2; - config_nand_cw_write(chip); + qcom_nandc_config_cw_write(nandc); } ret = qcom_submit_descs(nandc); @@ -1144,10 +1010,10 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false, 0); - config_nand_page_write(chip); + qcom_nandc_config_page_write(nandc); qcom_write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, data_size + oob_size, 0); - config_nand_cw_write(chip); + qcom_nandc_config_cw_write(nandc); ret = qcom_submit_descs(nandc); if (ret) { @@ -1181,7 +1047,7 @@ static int qcom_nandc_block_bad(struct nand_chip *chip, loff_t ofs) if (ret) goto err; - if (check_flash_errors(host, 1)) { + if (qcom_nandc_check_flash_errors(nandc, 1)) { dev_warn(nandc->dev, "error when trying to read BBM\n"); goto err; } @@ -1220,10 +1086,10 @@ static int qcom_nandc_block_markbad(struct nand_chip *chip, loff_t ofs) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false, ecc->steps - 1); - config_nand_page_write(chip); + qcom_nandc_config_page_write(nandc); qcom_write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, host->cw_size, 0); - config_nand_cw_write(chip); + qcom_nandc_config_cw_write(nandc); ret = qcom_submit_descs(nandc); if (ret) { @@ -1921,9 +1787,9 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_ len = nand_subop_get_data_len(subop, op_id); if (nandc->props->qpic_version2) - nandc_set_read_loc_last(chip, reg_base, 0, len, 1); + qcom_nandc_set_read_loc_last(nandc, reg_base, 0, len, 1); else - nandc_set_read_loc_first(chip, reg_base, 0, len, 1); + qcom_nandc_set_read_loc_first(nandc, reg_base, 0, len, 1); if (!nandc->props->has_onfi_read_op) { qcom_write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0); diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index c08d6b176372..9f42f8ab3212 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -2076,7 +2076,7 @@ static void stm32_fmc2_nfc_remove(struct platform_device *pdev) stm32_fmc2_nfc_wp_enable(nand); } -static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev) +static int stm32_fmc2_nfc_suspend(struct device *dev) { struct stm32_fmc2_nfc *nfc = dev_get_drvdata(dev); struct stm32_fmc2_nand *nand = &nfc->nand; @@ -2090,7 +2090,7 @@ static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev) return 0; } -static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev) +static int stm32_fmc2_nfc_resume(struct device *dev) { struct stm32_fmc2_nfc *nfc = dev_get_drvdata(dev); struct stm32_fmc2_nand *nand = &nfc->nand; @@ -2118,7 +2118,7 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(stm32_fmc2_nfc_pm_ops, stm32_fmc2_nfc_suspend, +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_fmc2_nfc_pm_ops, stm32_fmc2_nfc_suspend, stm32_fmc2_nfc_resume); static const struct stm32_fmc2_nfc_data stm32_fmc2_nfc_mp1_data = { @@ -2153,7 +2153,7 @@ static struct platform_driver stm32_fmc2_nfc_driver = { .driver = { .name = "stm32_fmc2_nfc", .of_match_table = stm32_fmc2_nfc_match, - .pm = &stm32_fmc2_nfc_pm_ops, + .pm = pm_sleep_ptr(&stm32_fmc2_nfc_pm_ops), }, }; module_platform_driver(stm32_fmc2_nfc_driver); diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 45ccbce91551..f41feebe4257 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -222,6 +222,7 @@ * USER_DATA_LEN registers. */ #define USER_DATA_SZ 4 +#define SUNXI_NFC_MAX_USER_DATA_SZ 32 /** * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select @@ -1003,11 +1004,10 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); - u8 *user_data = NULL; + u8 user_data[SUNXI_NFC_MAX_USER_DATA_SZ] = {}; /* Randomize the Bad Block Marker. */ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { - user_data = kmalloc(user_data_sz, GFP_KERNEL); memcpy(user_data, oob, user_data_sz); sunxi_nfc_randomize_bbm(nand, page, user_data); oob = user_data; @@ -1040,7 +1040,6 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, } } - kfree(user_data); } static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, @@ -1991,7 +1990,7 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, section); + unsigned int user_data_sz; /* * The controller does not provide access to OOB bytes @@ -2000,6 +1999,8 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, if (section >= ecc->steps) return -ERANGE; + user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, section); + /* * The first 2 bytes are used for BB markers, hence we * only have user_data_sz - 2 bytes available in the first user data @@ -2007,12 +2008,12 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, */ if (section == 0) { oobregion->offset = 2; - oobregion->length = user_data_sz - 2; + oobregion->length = user_data_sz > 2 ? user_data_sz - 2 : 0; return 0; } - oobregion->offset = sunxi_get_ecc_offset(sunxi_nand, ecc, section); + oobregion->offset = sunxi_get_oob_offset(sunxi_nand, ecc, section); oobregion->length = user_data_sz; return 0; @@ -2041,6 +2042,9 @@ static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize int remaining_bytes = oobsize - (ecc_bytes * nsectors); int i, step; + if (nsectors <= 0) + return -EINVAL; + sunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors, GFP_KERNEL); if (!sunxi_nand->user_data_bytes) @@ -2056,6 +2060,8 @@ static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize if (sunxi_nand->user_data_bytes[step] == 0) break; } + if (sunxi_nand->user_data_bytes[0] < USER_DATA_SZ) + return -EINVAL; return 0; } @@ -2072,11 +2078,15 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, int total_user_data_sz = 0; int nsectors; int ecc_mode; + int ret; int i; if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { int bytes = mtd->oobsize; + if (mtd->writesize < 1024) + return -EINVAL; + ecc->size = 1024; nsectors = mtd->writesize / ecc->size; @@ -2102,10 +2112,10 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, bytes -= total_user_data_sz; } else { /* - * remove at least the BBM size before computing the - * max ECC + * User-data lengths are encoded in four-byte units. Reserve + * the first word because it contains the two BBM bytes. */ - bytes -= 2; + bytes -= USER_DATA_SZ; } /* @@ -2169,9 +2179,12 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, * The rationale for variable data length is to prioritize maximum ECC * strength, and then use the remaining space for user data. */ - if (nfc->caps->reg_user_data_len) - sunxi_nfc_maximize_user_data(nand, mtd->oobsize, ecc->bytes, - nsectors); + if (nfc->caps->reg_user_data_len) { + ret = sunxi_nfc_maximize_user_data(nand, mtd->oobsize, + ecc->bytes, nsectors); + if (ret) + return ret; + } if (total_user_data_sz == 0) for (i = 0; i < nsectors; i++) diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index b5ccb44860df..f6b2a094fafc 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 spinand-objs := core.o otp.o spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o heyangtek.o -spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o +spinand-objs += issi.o macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 8bf9301f25e7..86a8d759682b 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1360,6 +1360,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &foresee_spinand_manufacturer, &gigadevice_spinand_manufacturer, &heyangtek_spinand_manufacturer, + &issi_spinand_manufacturer, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, ¶gon_spinand_manufacturer, diff --git a/drivers/mtd/nand/spi/heyangtek.c b/drivers/mtd/nand/spi/heyangtek.c index 7fc50fd3de08..76d97ae9702c 100644 --- a/drivers/mtd/nand/spi/heyangtek.c +++ b/drivers/mtd/nand/spi/heyangtek.c @@ -35,8 +35,8 @@ static SPINAND_OP_VARIANTS(update_cache_variants, /* * HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken * from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each - * 16-byte section, the bad block marker sits in byte 0 and the remaining - * bytes are exposed as free. + * 16-byte section, and the remaining bytes are exposed as free, except the + * two bytes that spinand_isbad() reads as the bad block marker. */ static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) @@ -60,9 +60,9 @@ static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section, region->offset = 16 * section; region->length = 8; } else { - /* section 0 has one byte reserved for the bad block marker */ - region->offset = 1; - region->length = 7; + /* section 0 has two bytes reserved for the bad block marker */ + region->offset = 2; + region->length = 6; } return 0; diff --git a/drivers/mtd/nand/spi/issi.c b/drivers/mtd/nand/spi/issi.c new file mode 100644 index 000000000000..122b3a6e2f62 --- /dev/null +++ b/drivers/mtd/nand/spi/issi.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 ISSI + * + * Authors: + * Bill Lee <blee@issi.com> + * Jeff Kim <jekim@issi.com> + * Co-Author: + * Han Xu <han.xu@nxp.com> + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mtd/spinand.h> + +#define SPINAND_MFR_ISSI 0x9d + +#define ISSI_STATUS_ECC_MASK GENMASK(6, 4) +#define ISSI_STATUS_ECC_NO_BITFLIPS 0 +#define ISSI_STATUS_ECC_1TO3_BITFLIPS 1 +#define ISSI_STATUS_ECC_UNCOR_ERROR 2 +#define ISSI_STATUS_ECC_4TO6_BITFLIPS 3 +#define ISSI_STATUS_ECC_7TO8_BITFLIPS 5 + +/* + * As per datasheet, die selection is done by the 7th bit of Drive + * Strength Register (Address 0xD0). + */ +#define ISSI_DIE_SELECT_REG 0xD0 +#define ISSI_SELECT_DIE_MASK BIT(7) +#define ISSI_SELECT_DIE(x) ((x) << 7) + +static SPINAND_OP_VARIANTS( + quadio_read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); + +static SPINAND_OP_VARIANTS(x4_write_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(x4_update_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + +static int issi_8_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = mtd->oobsize / 2; + region->length = mtd->oobsize / 2; + + return 0; +} + +static int issi_8_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; + region->length = (mtd->oobsize / 2) - 2; + + return 0; +} + +static const struct mtd_ooblayout_ops issi_8_ooblayout = { + .ecc = issi_8_ooblayout_ecc, + .free = issi_8_ooblayout_free, +}; + +static int issi_select_target(struct spinand_device *spinand, + unsigned int target) +{ + int ret; + u8 regval; + + if (target > 1) + return -EINVAL; + + ret = spinand_read_reg_op(spinand, ISSI_DIE_SELECT_REG, ®val); + if (ret) + return ret; + + regval &= ~ISSI_SELECT_DIE_MASK; + regval |= ISSI_SELECT_DIE(target); + + return spinand_write_reg_op(spinand, ISSI_DIE_SELECT_REG, regval); +} + +static int issi_8_ecc_get_status(struct spinand_device *spinand, u8 status) +{ + switch (FIELD_GET(ISSI_STATUS_ECC_MASK, status)) { + case ISSI_STATUS_ECC_NO_BITFLIPS: + return 0; + + case ISSI_STATUS_ECC_1TO3_BITFLIPS: + return 3; + + case ISSI_STATUS_ECC_4TO6_BITFLIPS: + return 6; + + case ISSI_STATUS_ECC_7TO8_BITFLIPS: + return 8; + + case ISSI_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info issi_spinand_table[] = { + /* IS37/38SMW01G8B 1Gb 1.8V */ + SPINAND_INFO("IS37/38SMW01G8B", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), + NAND_MEMORG(1, 2048, 128, 64, 1024, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status), + SPINAND_SELECT_TARGET(issi_select_target)), + + /* IS37/38SMW02G8B 2Gb 1.8V */ + SPINAND_INFO("IS37/38SMW02G8B", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status), + SPINAND_SELECT_TARGET(issi_select_target)), + + /* IS37/38SML04G8B 4Gb 3.3V */ + SPINAND_INFO("IS37/38SML04G8", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status), + SPINAND_SELECT_TARGET(issi_select_target)), + + /* IS37/38SMW04G8B 4Gb 1.8V */ + SPINAND_INFO("IS37/38SMW04G8B", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status), + SPINAND_SELECT_TARGET(issi_select_target)), +}; + +static int issi_spinand_init(struct spinand_device *spinand) +{ + return 0; +} + +static const struct spinand_manufacturer_ops issi_spinand_manuf_ops = { + .init = issi_spinand_init, +}; + +const struct spinand_manufacturer issi_spinand_manufacturer = { + .id = SPINAND_MFR_ISSI, + .name = "ISSI", + .chips = issi_spinand_table, + .nchips = ARRAY_SIZE(issi_spinand_table), + .ops = &issi_spinand_manuf_ops, +}; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 9b78c1e6cbc9..810975808c97 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -211,6 +211,13 @@ static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); +static SPINAND_OP_VARIANTS(cont_read_cache_variants, + WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(6, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(4, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(4, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(4, NULL, 0, 0), + WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(4, NULL, 0, 0)); + static SPINAND_OP_VARIANTS(write_cache_variants, SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); @@ -314,10 +321,18 @@ static int w25n01kv_ooblayout_ecc(struct mtd_info *mtd, int section, static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { - if (section > 3) + int num_sections = 4; + int offset = 64; + + if (mtd->oobsize == 256) { + num_sections = 8; + offset = 128; + } + + if (section >= num_sections) return -ERANGE; - region->offset = 64 + (16 * section); + region->offset = offset + (16 * section); region->length = 13; return 0; @@ -326,9 +341,21 @@ static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section, static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { - if (section > 3) + int num_sections = 4; + + if (mtd->oobsize == 256) + num_sections = 8; + + if (section >= num_sections) return -ERANGE; + /* + * For at least W25N04KW and W25N04LW, this region is actually + * split in two: + * ECC-protected "User Data I" at + 4 offset, length 12 + * unprotected "User Data II" at + 2 offset, length 2 + * This returns the full region for backwards compatibility. + */ region->offset = (16 * section) + 2; region->length = 14; @@ -345,6 +372,39 @@ static const struct mtd_ooblayout_ops w25n02kv_ooblayout = { .free = w25n02kv_ooblayout_free, }; +static int w25n08lw_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + /* + * With ecc enabled the parity bits are not accessible. So we can + * only see page + 128. Without ecc the full page + 256 is accessible. + * To make it simple just return the area as not accessible. + */ + return -ERANGE; +} + +static int w25n08lw_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 7) + return -ERANGE; + + /* + * Only expose the ECC protected "User Data I" bytes. The "User Data II" + * bytes at offset + 2 are not covered by the on-die ECC engine, and the + * first two bytes of the section hold the BBM. + */ + region->offset = (16 * section) + 4; + region->length = 12; + + return 0; +} + +static const struct mtd_ooblayout_ops w25n08lw_ooblayout = { + .ecc = w25n08lw_ooblayout_ecc, + .free = w25n08lw_ooblayout_free, +}; + static int w25n01jw_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { @@ -768,6 +828,15 @@ static const struct spinand_info winbond_spinand_table[] = { &update_cache_variants), 0, SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)), + SPINAND_INFO("W25N04LW", /* 1.8V */ + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb2, 0x23), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)), SPINAND_INFO("W35N04JW", /* 1.8V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23), NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1), @@ -781,6 +850,18 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status), SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg), SPINAND_CONT_READ(w35n0xjw_set_cont_read)), + /* 8G-bit densities */ + SPINAND_INFO("W25N08LW", /* 2x4G-bit 1.8V */ + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb3, 0x24), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 2, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_variants, + &write_cache_variants, + &update_cache_variants, + &cont_read_cache_variants), + 0, + SPINAND_ECCINFO(&w25n08lw_ooblayout, w25n02kv_ecc_get_status), + SPINAND_CONT_READ(w25n0xjw_set_cont_read)), }; static int winbond_spinand_init(struct spinand_device *spinand) diff --git a/include/linux/mtd/lpc32xx_mlc.h b/include/linux/mtd/lpc32xx_mlc.h deleted file mode 100644 index 35e971be0950..000000000000 --- a/include/linux/mtd/lpc32xx_mlc.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Platform data for LPC32xx SoC MLC NAND controller - * - * Copyright © 2012 Roland Stigge - */ - -#ifndef __LINUX_MTD_LPC32XX_MLC_H -#define __LINUX_MTD_LPC32XX_MLC_H - -#include <linux/dmaengine.h> - -struct lpc32xx_mlc_platform_data { - dma_filter_fn dma_filter; -}; - -#endif /* __LINUX_MTD_LPC32XX_MLC_H */ diff --git a/include/linux/mtd/lpc32xx_slc.h b/include/linux/mtd/lpc32xx_slc.h deleted file mode 100644 index a044b806566b..000000000000 --- a/include/linux/mtd/lpc32xx_slc.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Platform data for LPC32xx SoC SLC NAND controller - * - * Copyright © 2012 Roland Stigge - */ - -#ifndef __LINUX_MTD_LPC32XX_SLC_H -#define __LINUX_MTD_LPC32XX_SLC_H - -#include <linux/dmaengine.h> - -struct lpc32xx_slc_platform_data { - dma_filter_fn dma_filter; -}; - -#endif /* __LINUX_MTD_LPC32XX_SLC_H */ diff --git a/include/linux/mtd/nand-qpic-common.h b/include/linux/mtd/nand-qpic-common.h index 437448995187..f31fd18d837f 100644 --- a/include/linux/mtd/nand-qpic-common.h +++ b/include/linux/mtd/nand-qpic-common.h @@ -262,7 +262,6 @@ struct bam_transaction { u32 tx_sgl_start; u32 rx_sgl_pos; u32 rx_sgl_start; - ); }; @@ -458,6 +457,12 @@ struct qcom_nandc_props { bool use_codeword_fixup; }; +struct qcom_nandc_read_stats { + __le32 flash; + __le32 buffer; + __le32 erased_cw; +}; + void qcom_free_bam_transaction(struct qcom_nand_controller *nandc); struct bam_transaction *qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc); void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc); @@ -483,5 +488,16 @@ int qcom_submit_descs(struct qcom_nand_controller *nandc); void qcom_clear_read_regs(struct qcom_nand_controller *nandc); void qcom_nandc_unalloc(struct qcom_nand_controller *nandc); int qcom_nandc_alloc(struct qcom_nand_controller *nandc); +int qcom_nandc_check_flash_errors(struct qcom_nand_controller *nandc, + int cw_cnt); +void qcom_nandc_config_page_read(struct qcom_nand_controller *nandc); +void qcom_nandc_config_page_write(struct qcom_nand_controller *nandc); +void qcom_nandc_config_cw_write(struct qcom_nand_controller *nandc); +void qcom_nandc_set_read_loc_first(struct qcom_nand_controller *nandc, + int reg_base, u32 cw_offset, + u32 read_size, u32 is_last_read_loc); +void qcom_nandc_set_read_loc_last(struct qcom_nand_controller *nandc, + int reg_base, u32 cw_offset, + u32 read_size, u32 is_last_read_loc); #endif diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 5f4c00ae72a7..d7c99fcfb85e 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -399,7 +399,7 @@ struct spinand_devid { }; /** - * struct manufacurer_ops - SPI NAND manufacturer specific operations + * struct spinand_manufacturer_ops - SPI NAND manufacturer specific operations * @init: initialize a SPI NAND device * @cleanup: cleanup a SPI NAND device * @@ -438,6 +438,7 @@ extern const struct spinand_manufacturer fmsh_spinand_manufacturer; extern const struct spinand_manufacturer foresee_spinand_manufacturer; extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; extern const struct spinand_manufacturer heyangtek_spinand_manufacturer; +extern const struct spinand_manufacturer issi_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; extern const struct spinand_manufacturer paragon_spinand_manufacturer; |
