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.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmrbj2oACgkQJWrqGEe9
 VoRTzwf+NmPfqwnoCAzGjX6FpvDHfM8GHW5MjmqqAZ0ogX3lOyj+ysCqa4LgQbJO
 w2KPGE5zy3K4NmcXYlpjH9kJ0HReQltDL5/C3pb6vMDzWm6TRlScdeG/BYYiHf0q
 lCQEPZA6gbd1Jwp0is6Q6QEJR3JfuAZACL+4Dv6sBLAPf1/f5Q0mJ/v3br/rL0rM
 01cLr39DhzeKMSKUR9TVYayCU0VoLOvNEmZOHB/v4UGF7vWP0vLv4hTygBGXl8oP
 S7pJzSytuB4edre7MKbEl49APeDw4fXiSnjKCcmZ6i3Xv15SnLcxJLQybZePtUSF
 7bLGL7MEvB2Vi34k2koO7uP0ipWdyQ==
 =+FCW
 -----END PGP SIGNATURE-----

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.
This commit is contained in:
Miquel Raynal 2026-10-23 18:49:30 +02:00
commit 76ebe529a4
31 changed files with 691 additions and 369 deletions

View File

@ -35,6 +35,9 @@ properties:
interrupts:
maxItems: 1
power-domains:
maxItems: 1
required:
- compatible
- reg

View File

@ -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>;
};
};
};

View File

@ -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>;
};
};
};
};
};
...

View File

@ -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;
}

View File

@ -775,5 +775,136 @@ int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
}
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");

View File

@ -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);

View File

@ -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 {

View File

@ -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[] = {

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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"},

View File

@ -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[] = {

View File

@ -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;
}

View File

@ -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];

View File

@ -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

View File

@ -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 */
@ -301,24 +239,6 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, i
host->cw_data : host->cw_size, 1);
}
/*
* 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.
@ -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,43 +279,12 @@ static void
config_nand_single_cw_page_read(struct nand_chip *chip,
bool use_ecc, int cw)
{
config_nand_page_read(chip);
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
qcom_nandc_config_page_read(nandc);
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);
}
/*
* when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read
* an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS.
@ -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);

View File

@ -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);

View File

@ -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++)

View File

@ -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

View File

@ -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,

View File

@ -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;

187
drivers/mtd/nand/spi/issi.c Normal file
View File

@ -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,
};

View File

@ -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)

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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;