mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
76ebe529a4
|
|
@ -35,6 +35,9 @@ properties:
|
|||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
128
Documentation/devicetree/bindings/mtd/ibm,ndfc.yaml
Normal file
128
Documentation/devicetree/bindings/mtd/ibm,ndfc.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1360,6 +1360,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
|
|||
&foresee_spinand_manufacturer,
|
||||
&gigadevice_spinand_manufacturer,
|
||||
&heyangtek_spinand_manufacturer,
|
||||
&issi_spinand_manufacturer,
|
||||
¯onix_spinand_manufacturer,
|
||||
µn_spinand_manufacturer,
|
||||
¶gon_spinand_manufacturer,
|
||||
|
|
|
|||
|
|
@ -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
187
drivers/mtd/nand/spi/issi.c
Normal 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, ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regval &= ~ISSI_SELECT_DIE_MASK;
|
||||
regval |= ISSI_SELECT_DIE(target);
|
||||
|
||||
return spinand_write_reg_op(spinand, ISSI_DIE_SELECT_REG, regval);
|
||||
}
|
||||
|
||||
static int issi_8_ecc_get_status(struct spinand_device *spinand, u8 status)
|
||||
{
|
||||
switch (FIELD_GET(ISSI_STATUS_ECC_MASK, status)) {
|
||||
case ISSI_STATUS_ECC_NO_BITFLIPS:
|
||||
return 0;
|
||||
|
||||
case ISSI_STATUS_ECC_1TO3_BITFLIPS:
|
||||
return 3;
|
||||
|
||||
case ISSI_STATUS_ECC_4TO6_BITFLIPS:
|
||||
return 6;
|
||||
|
||||
case ISSI_STATUS_ECC_7TO8_BITFLIPS:
|
||||
return 8;
|
||||
|
||||
case ISSI_STATUS_ECC_UNCOR_ERROR:
|
||||
return -EBADMSG;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct spinand_info issi_spinand_table[] = {
|
||||
/* IS37/38SMW01G8B 1Gb 1.8V */
|
||||
SPINAND_INFO("IS37/38SMW01G8B",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status),
|
||||
SPINAND_SELECT_TARGET(issi_select_target)),
|
||||
|
||||
/* IS37/38SMW02G8B 2Gb 1.8V */
|
||||
SPINAND_INFO("IS37/38SMW02G8B",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status),
|
||||
SPINAND_SELECT_TARGET(issi_select_target)),
|
||||
|
||||
/* IS37/38SML04G8B 4Gb 3.3V */
|
||||
SPINAND_INFO("IS37/38SML04G8",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 2),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status),
|
||||
SPINAND_SELECT_TARGET(issi_select_target)),
|
||||
|
||||
/* IS37/38SMW04G8B 4Gb 1.8V */
|
||||
SPINAND_INFO("IS37/38SMW04G8B",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 2),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status),
|
||||
SPINAND_SELECT_TARGET(issi_select_target)),
|
||||
};
|
||||
|
||||
static int issi_spinand_init(struct spinand_device *spinand)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spinand_manufacturer_ops issi_spinand_manuf_ops = {
|
||||
.init = issi_spinand_init,
|
||||
};
|
||||
|
||||
const struct spinand_manufacturer issi_spinand_manufacturer = {
|
||||
.id = SPINAND_MFR_ISSI,
|
||||
.name = "ISSI",
|
||||
.chips = issi_spinand_table,
|
||||
.nchips = ARRAY_SIZE(issi_spinand_table),
|
||||
.ops = &issi_spinand_manuf_ops,
|
||||
};
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user