summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2026-10-23 18:49:30 +0200
committerMiquel Raynal <miquel.raynal@bootlin.com>2026-10-23 18:49:30 +0200
commit76ebe529a48518d0fa671091a8c2f1a01069aa49 (patch)
tree26dd81a02d188de286ee5e25d17194f30fb8d9b1
parent2f7fa8d90694d21dce68ceabd8383252b111cfea (diff)
parent30daaf3e7897b4a8d9b72e5b0c86dd6e2a4d7d85 (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.
-rw-r--r--Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml3
-rw-r--r--Documentation/devicetree/bindings/mtd/ibm,ndfc.txt39
-rw-r--r--Documentation/devicetree/bindings/mtd/ibm,ndfc.yaml128
-rw-r--r--drivers/mtd/nand/onenand/onenand_omap2.c4
-rw-r--r--drivers/mtd/nand/qpic_common.c131
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c6
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.c2
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_nand.c49
-rw-r--r--drivers/mtd/nand/raw/fsmc_nand.c2
-rw-r--r--drivers/mtd/nand/raw/intel-nand-controller.c2
-rw-r--r--drivers/mtd/nand/raw/internals.h1
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_mlc.c21
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_slc.c21
-rw-r--r--drivers/mtd/nand/raw/nand_esmt.c34
-rw-r--r--drivers/mtd/nand/raw/nand_ids.c2
-rw-r--r--drivers/mtd/nand/raw/ndfc.c28
-rw-r--r--drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c1
-rw-r--r--drivers/mtd/nand/raw/omap2.c4
-rw-r--r--drivers/mtd/nand/raw/pl35x-nand-controller.c1
-rw-r--r--drivers/mtd/nand/raw/qcom_nandc.c192
-rw-r--r--drivers/mtd/nand/raw/stm32_fmc2_nand.c8
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c37
-rw-r--r--drivers/mtd/nand/spi/Makefile2
-rw-r--r--drivers/mtd/nand/spi/core.c1
-rw-r--r--drivers/mtd/nand/spi/heyangtek.c10
-rw-r--r--drivers/mtd/nand/spi/issi.c187
-rw-r--r--drivers/mtd/nand/spi/winbond.c87
-rw-r--r--include/linux/mtd/lpc32xx_mlc.h17
-rw-r--r--include/linux/mtd/lpc32xx_slc.h17
-rw-r--r--include/linux/mtd/nand-qpic-common.h18
-rw-r--r--include/linux/mtd/spinand.h3
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,
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
&paragon_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, &regval);
+ 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;