NAND core:

* Print offset instead of page number for bad blocks
 
 Raw NAND controller drivers:
 * Cadence: Fix possible null-ptr-deref in cadence_nand_dt_probe()
 * CS553X: simplify the return expression of cs553x_write_ctrl_byte()
 * Davinci: Remove redundant unsigned comparison to zero
 * Denali: Use managed device resources
 * GPMI:
   - Add large oob bch setting support
   - Rename the variable ecc_chunk_size
   - Uninline the gpmi_check_ecc function
   - Add strict ecc strength check
   - Refactor BCH geometry settings function
 * Intel: Fix possible null-ptr-deref in ebu_nand_probe()
 * MPC5121: Check before clk_disable_unprepare() not needed
 * Mtk:
   - MTD_NAND_ECC_MEDIATEK should depend on ARCH_MEDIATEK
   - Also parse the default nand-ecc-engine property if available
   - Make mtk_ecc.c a separated module
 * OMAP ELM:
   - Convert the bindings to yaml
   - Describe the bindings for AM64 ELM
   - Add support for its compatible
 * Renesas: Use runtime PM instead of the raw clock API and update the
            bindings accordingly
 * Rockchip: Check before clk_disable_unprepare() not needed
 * TMIO: Check return value after calling platform_get_resource()
 
 Raw NAND chip driver:
 * Kioxia: Add support for TH58NVG3S0HBAI4 and TC58NVG0S3HTA00
 
 SPI-NAND chip drivers:
 * Gigadevice:
   - Add support for:
     - GD5FxGM7xExxG
     - GD5F{2,4}GQ5xExxG
     - GD5F1GQ5RExxG
     - GD5FxGQ4xExxG
   - Fix Quad IO for GD5F1GQ5UExxG
 * XTX: Add support for XT26G0xA
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmKD51AACgkQJWrqGEe9
 VoQJ8AgAo8qjO5QQmd76v1q1Regxxl7RsH7hr7r9gJfgTwzhQC/xIyTiorcVPG7B
 UDNu4Dwe3OAPbmU54TQNS/AMQkgHGMcIRTVut8+oL+ZnjYl+gv261GxmsfxaK/Hu
 Vvvq9X0iyaKpZfyq3uksbsxiXbwMn4fHT7Reaimc9Thw+XKD7AcYWCuFb9GAWfaf
 XQUVLg6y4Qk4BR9ZpYAx2v5FH4amJV9RKKTIqiymwcjnBZjYOI29wgKwY1hX+3bm
 2Wu4wcccWhDzlV0Casf/hIGBydKx3omV+cJHLtmx7s+dqPvYSUGuvR2nbq5wuMqp
 ZcAYeRhGEAYMYcMB/QuKmh4g/Js26w==
 =QyqB
 -----END PGP SIGNATURE-----

Merge tag 'nand/for-5.19' into mtd/next

NAND core:
* Print offset instead of page number for bad blocks

Raw NAND controller drivers:
* Cadence: Fix possible null-ptr-deref in cadence_nand_dt_probe()
* CS553X: simplify the return expression of cs553x_write_ctrl_byte()
* Davinci: Remove redundant unsigned comparison to zero
* Denali: Use managed device resources
* GPMI:
  - Add large oob bch setting support
  - Rename the variable ecc_chunk_size
  - Uninline the gpmi_check_ecc function
  - Add strict ecc strength check
  - Refactor BCH geometry settings function
* Intel: Fix possible null-ptr-deref in ebu_nand_probe()
* MPC5121: Check before clk_disable_unprepare() not needed
* Mtk:
  - MTD_NAND_ECC_MEDIATEK should depend on ARCH_MEDIATEK
  - Also parse the default nand-ecc-engine property if available
  - Make mtk_ecc.c a separated module
* OMAP ELM:
  - Convert the bindings to yaml
  - Describe the bindings for AM64 ELM
  - Add support for its compatible
* Renesas: Use runtime PM instead of the raw clock API and update the
           bindings accordingly
* Rockchip: Check before clk_disable_unprepare() not needed
* TMIO: Check return value after calling platform_get_resource()

Raw NAND chip driver:
* Kioxia: Add support for TH58NVG3S0HBAI4 and TC58NVG0S3HTA00

SPI-NAND chip drivers:
* Gigadevice:
  - Add support for:
    - GD5FxGM7xExxG
    - GD5F{2,4}GQ5xExxG
    - GD5F1GQ5RExxG
    - GD5FxGQ4xExxG
  - Fix Quad IO for GD5F1GQ5UExxG
* XTX: Add support for XT26G0xA

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
This commit is contained in:
Miquel Raynal 2022-05-20 13:59:25 +02:00
commit 2c51d0d880
30 changed files with 702 additions and 130 deletions

View File

@ -1,16 +0,0 @@
Error location module
Required properties:
- compatible: Must be "ti,am3352-elm"
- reg: physical base address and size of the registers map.
- interrupts: Interrupt number for the elm.
Optional properties:
- ti,hwmods: Name of the hwmod associated to the elm
Example:
elm: elm@0 {
compatible = "ti,am3352-elm";
reg = <0x48080000 0x2000>;
interrupts = <4>;
};

View File

@ -36,11 +36,15 @@ properties:
- const: hclk
- const: eclk
power-domains:
maxItems: 1
required:
- compatible
- reg
- clocks
- clock-names
- power-domains
- interrupts
unevaluatedProperties: false
@ -56,6 +60,7 @@ examples:
interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
clock-names = "hclk", "eclk";
power-domains = <&sysctrl>;
#address-cells = <1>;
#size-cells = <0>;
};

View File

@ -0,0 +1,72 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/ti,elm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments Error Location Module (ELM).
maintainers:
- Roger Quadros <rogerq@kernel.org>
description:
ELM module is used together with GPMC and NAND Flash to detect
errors and the location of the error based on BCH algorithms
so they can be corrected if possible.
properties:
compatible:
enum:
- ti,am3352-elm
- ti,am64-elm
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
description: Functional clock.
clock-names:
items:
- const: fck
power-domains:
maxItems: 1
ti,hwmods:
description:
Name of the HWMOD associated with ELM. This is for legacy
platforms only.
$ref: /schemas/types.yaml#/definitions/string
deprecated: true
required:
- compatible
- reg
- interrupts
allOf:
- if:
properties:
compatible:
contains:
const: ti,am64-elm
then:
required:
- clocks
- clock-names
- power-domains
additionalProperties: false
examples:
- |
elm: ecc@0 {
compatible = "ti,am3352-elm";
reg = <0x0 0x2000>;
interrupts = <4>;
};

View File

@ -53,6 +53,14 @@ config MTD_NAND_ECC_MXIC
help
This enables support for the hardware ECC engine from Macronix.
config MTD_NAND_ECC_MEDIATEK
tristate "Mediatek hardware ECC engine"
depends on HAS_IOMEM
depends on ARCH_MEDIATEK || COMPILE_TEST
select MTD_NAND_ECC
help
This enables support for the hardware ECC engine from Mediatek.
endmenu
endmenu

View File

@ -2,6 +2,7 @@
nandcore-objs := core.o bbt.o
obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o
obj-y += onenand/
obj-y += raw/

View File

@ -15,8 +15,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/mutex.h>
#include "mtk_ecc.h"
#include <linux/mtd/nand-ecc-mtk.h>
#define ECC_IDLE_MASK BIT(0)
#define ECC_IRQ_EN BIT(0)
@ -279,7 +278,10 @@ struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node)
struct mtk_ecc *ecc = NULL;
struct device_node *np;
np = of_parse_phandle(of_node, "ecc-engine", 0);
np = of_parse_phandle(of_node, "nand-ecc-engine", 0);
/* for backward compatibility */
if (!np)
np = of_parse_phandle(of_node, "ecc-engine", 0);
if (np) {
ecc = mtk_ecc_get(np);
of_node_put(np);

View File

@ -374,6 +374,7 @@ config MTD_NAND_QCOM
config MTD_NAND_MTK
tristate "MTK NAND controller"
depends on MTD_NAND_ECC_MEDIATEK
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on HAS_IOMEM
help

View File

@ -48,7 +48,7 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o
obj-$(CONFIG_MTD_NAND_MXIC) += mxic_nand.o
obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o

View File

@ -2983,11 +2983,10 @@ static int cadence_nand_dt_probe(struct platform_device *ofdev)
if (IS_ERR(cdns_ctrl->reg))
return PTR_ERR(cdns_ctrl->reg);
res = platform_get_resource(ofdev, IORESOURCE_MEM, 1);
cdns_ctrl->io.dma = res->start;
cdns_ctrl->io.virt = devm_ioremap_resource(&ofdev->dev, res);
cdns_ctrl->io.virt = devm_platform_get_and_ioremap_resource(ofdev, 1, &res);
if (IS_ERR(cdns_ctrl->io.virt))
return PTR_ERR(cdns_ctrl->io.virt);
cdns_ctrl->io.dma = res->start;
dt->clk = devm_clk_get(cdns_ctrl->dev, "nf_clk");
if (IS_ERR(dt->clk))

View File

@ -104,17 +104,12 @@ static int cs553x_write_ctrl_byte(struct cs553x_nand_controller *cs553x,
u32 ctl, u8 data)
{
u8 status;
int ret;
writeb(ctl, cs553x->mmio + MM_NAND_CTL);
writeb(data, cs553x->mmio + MM_NAND_IO);
ret = readb_poll_timeout_atomic(cs553x->mmio + MM_NAND_STS, status,
return readb_poll_timeout_atomic(cs553x->mmio + MM_NAND_STS, status,
!(status & CS_NAND_CTLR_BUSY), 1,
100000);
if (ret)
return ret;
return 0;
}
static void cs553x_data_in(struct cs553x_nand_controller *cs553x, void *buf,

View File

@ -727,7 +727,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
return -ENODEV;
/* which external chipselect will we be managing? */
if (pdata->core_chipsel < 0 || pdata->core_chipsel > 3)
if (pdata->core_chipsel > 3)
return -ENODEV;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);

View File

@ -74,22 +74,21 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
return ret;
}
denali->reg = ioremap(csr_base, csr_len);
denali->reg = devm_ioremap(denali->dev, csr_base, csr_len);
if (!denali->reg) {
dev_err(&dev->dev, "Spectra: Unable to remap memory region\n");
return -ENOMEM;
}
denali->host = ioremap(mem_base, mem_len);
denali->host = devm_ioremap(denali->dev, mem_base, mem_len);
if (!denali->host) {
dev_err(&dev->dev, "Spectra: ioremap failed!");
ret = -ENOMEM;
goto out_unmap_reg;
return -ENOMEM;
}
ret = denali_init(denali);
if (ret)
goto out_unmap_host;
return ret;
nsels = denali->nbanks;
@ -117,10 +116,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
out_remove_denali:
denali_remove(denali);
out_unmap_host:
iounmap(denali->host);
out_unmap_reg:
iounmap(denali->reg);
return ret;
}
@ -129,8 +124,6 @@ static void denali_pci_remove(struct pci_dev *dev)
struct denali_controller *denali = pci_get_drvdata(dev);
denali_remove(denali);
iounmap(denali->reg);
iounmap(denali->host);
}
static struct pci_driver denali_pci_driver = {

View File

@ -218,7 +218,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this)
"ECC Strength : %u\n"
"Page Size in Bytes : %u\n"
"Metadata Size in Bytes : %u\n"
"ECC Chunk Size in Bytes: %u\n"
"ECC0 Chunk Size in Bytes: %u\n"
"ECCn Chunk Size in Bytes: %u\n"
"ECC Chunk Count : %u\n"
"Payload Size in Bytes : %u\n"
"Auxiliary Size in Bytes: %u\n"
@ -229,7 +230,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this)
geo->ecc_strength,
geo->page_size,
geo->metadata_size,
geo->ecc_chunk_size,
geo->ecc0_chunk_size,
geo->eccn_chunk_size,
geo->ecc_chunk_count,
geo->payload_size,
geo->auxiliary_size,
@ -238,9 +240,15 @@ static void gpmi_dump_info(struct gpmi_nand_data *this)
geo->block_mark_bit_offset);
}
static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
static bool gpmi_check_ecc(struct gpmi_nand_data *this)
{
struct nand_chip *chip = &this->nand;
struct bch_geometry *geo = &this->bch_geometry;
struct nand_device *nand = &chip->base;
struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
conf->step_size = geo->eccn_chunk_size;
conf->strength = geo->ecc_strength;
/* Do the sanity check. */
if (GPMI_IS_MXS(this)) {
@ -248,7 +256,47 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
if (geo->gf_len == 14)
return false;
}
return geo->ecc_strength <= this->devdata->bch_max_ecc_strength;
if (geo->ecc_strength > this->devdata->bch_max_ecc_strength)
return false;
if (!nand_ecc_is_strong_enough(nand))
return false;
return true;
}
/* check if bbm locates in data chunk rather than ecc chunk */
static bool bbm_in_data_chunk(struct gpmi_nand_data *this,
unsigned int *chunk_num)
{
struct bch_geometry *geo = &this->bch_geometry;
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int i, j;
if (geo->ecc0_chunk_size != geo->eccn_chunk_size) {
dev_err(this->dev,
"The size of ecc0_chunk must equal to eccn_chunk\n");
return false;
}
i = (mtd->writesize * 8 - geo->metadata_size * 8) /
(geo->gf_len * geo->ecc_strength +
geo->eccn_chunk_size * 8);
j = (mtd->writesize * 8 - geo->metadata_size * 8) -
(geo->gf_len * geo->ecc_strength +
geo->eccn_chunk_size * 8) * i;
if (j < geo->eccn_chunk_size * 8) {
*chunk_num = i+1;
dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n",
geo->ecc_strength, *chunk_num);
return true;
}
return false;
}
/*
@ -280,13 +328,14 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
nanddev_get_ecc_requirements(&chip->base)->step_size);
return -EINVAL;
}
geo->ecc_chunk_size = ecc_step;
geo->ecc0_chunk_size = ecc_step;
geo->eccn_chunk_size = ecc_step;
geo->ecc_strength = round_up(ecc_strength, 2);
if (!gpmi_check_ecc(this))
return -EINVAL;
/* Keep the C >= O */
if (geo->ecc_chunk_size < mtd->oobsize) {
if (geo->eccn_chunk_size < mtd->oobsize) {
dev_err(this->dev,
"unsupported nand chip. ecc size: %d, oob size : %d\n",
ecc_step, mtd->oobsize);
@ -296,7 +345,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
/* The default value, see comment in the legacy_set_geometry(). */
geo->metadata_size = 10;
geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
/*
* Now, the NAND chip with 2K page(data chunk is 512byte) shows below:
@ -399,6 +448,134 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this)
return round_down(ecc_strength, 2);
}
static int set_geometry_for_large_oob(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
const struct nand_ecc_props *requirements =
nanddev_get_ecc_requirements(&chip->base);
unsigned int block_mark_bit_offset;
unsigned int max_ecc;
unsigned int bbm_chunk;
unsigned int i;
/* sanity check for the minimum ecc nand required */
if (!(requirements->strength > 0 &&
requirements->step_size > 0))
return -EINVAL;
geo->ecc_strength = requirements->strength;
/* check if platform can support this nand */
if (!gpmi_check_ecc(this)) {
dev_err(this->dev,
"unsupported NAND chip, minimum ecc required %d\n",
geo->ecc_strength);
return -EINVAL;
}
/* calculate the maximum ecc platform can support*/
geo->metadata_size = 10;
geo->gf_len = 14;
geo->ecc0_chunk_size = 1024;
geo->eccn_chunk_size = 1024;
geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
max_ecc = min(get_ecc_strength(this),
this->devdata->bch_max_ecc_strength);
/*
* search a supported ecc strength that makes bbm
* located in data chunk
*/
geo->ecc_strength = max_ecc;
while (!(geo->ecc_strength < requirements->strength)) {
if (bbm_in_data_chunk(this, &bbm_chunk))
goto geo_setting;
geo->ecc_strength -= 2;
}
/* if none of them works, keep using the minimum ecc */
/* nand required but changing ecc page layout */
geo->ecc_strength = requirements->strength;
/* add extra ecc for meta data */
geo->ecc0_chunk_size = 0;
geo->ecc_chunk_count = (mtd->writesize / geo->eccn_chunk_size) + 1;
geo->ecc_for_meta = 1;
/* check if oob can afford this extra ecc chunk */
if (mtd->oobsize * 8 < geo->metadata_size * 8 +
geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) {
dev_err(this->dev, "unsupported NAND chip with new layout\n");
return -EINVAL;
}
/* calculate in which chunk bbm located */
bbm_chunk = (mtd->writesize * 8 - geo->metadata_size * 8 -
geo->gf_len * geo->ecc_strength) /
(geo->gf_len * geo->ecc_strength +
geo->eccn_chunk_size * 8) + 1;
geo_setting:
geo->page_size = mtd->writesize + geo->metadata_size +
(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
geo->payload_size = mtd->writesize;
/*
* The auxiliary buffer contains the metadata and the ECC status. The
* metadata is padded to the nearest 32-bit boundary. The ECC status
* contains one byte for every ECC chunk, and is also padded to the
* nearest 32-bit boundary.
*/
geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
+ ALIGN(geo->ecc_chunk_count, 4);
if (!this->swap_block_mark)
return 0;
/* calculate the number of ecc chunk behind the bbm */
i = (mtd->writesize / geo->eccn_chunk_size) - bbm_chunk + 1;
block_mark_bit_offset = mtd->writesize * 8 -
(geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i)
+ geo->metadata_size * 8);
geo->block_mark_byte_offset = block_mark_bit_offset / 8;
geo->block_mark_bit_offset = block_mark_bit_offset % 8;
dev_dbg(this->dev, "BCH Geometry :\n"
"GF length : %u\n"
"ECC Strength : %u\n"
"Page Size in Bytes : %u\n"
"Metadata Size in Bytes : %u\n"
"ECC0 Chunk Size in Bytes: %u\n"
"ECCn Chunk Size in Bytes: %u\n"
"ECC Chunk Count : %u\n"
"Payload Size in Bytes : %u\n"
"Auxiliary Size in Bytes: %u\n"
"Auxiliary Status Offset: %u\n"
"Block Mark Byte Offset : %u\n"
"Block Mark Bit Offset : %u\n"
"Block Mark in chunk : %u\n"
"Ecc for Meta data : %u\n",
geo->gf_len,
geo->ecc_strength,
geo->page_size,
geo->metadata_size,
geo->ecc0_chunk_size,
geo->eccn_chunk_size,
geo->ecc_chunk_count,
geo->payload_size,
geo->auxiliary_size,
geo->auxiliary_status_offset,
geo->block_mark_byte_offset,
geo->block_mark_bit_offset,
bbm_chunk,
geo->ecc_for_meta);
return 0;
}
static int legacy_set_geometry(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
@ -418,13 +595,15 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
geo->gf_len = 13;
/* The default for chunk size. */
geo->ecc_chunk_size = 512;
while (geo->ecc_chunk_size < mtd->oobsize) {
geo->ecc_chunk_size *= 2; /* keep C >= O */
geo->ecc0_chunk_size = 512;
geo->eccn_chunk_size = 512;
while (geo->eccn_chunk_size < mtd->oobsize) {
geo->ecc0_chunk_size *= 2; /* keep C >= O */
geo->eccn_chunk_size *= 2; /* keep C >= O */
geo->gf_len = 14;
}
geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
/* We use the same ECC strength for all chunks. */
geo->ecc_strength = get_ecc_strength(this);
@ -514,24 +693,40 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
static int common_nfc_set_geometry(struct gpmi_nand_data *this)
{
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(&this->nand);
const struct nand_ecc_props *requirements =
nanddev_get_ecc_requirements(&chip->base);
bool use_minimun_ecc;
int err;
if (chip->ecc.strength > 0 && chip->ecc.size > 0)
return set_geometry_by_ecc_info(this, chip->ecc.strength,
chip->ecc.size);
use_minimun_ecc = of_property_read_bool(this->dev->of_node,
"fsl,use-minimum-ecc");
if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
|| legacy_set_geometry(this)) {
if (!(requirements->strength > 0 && requirements->step_size > 0))
return -EINVAL;
return set_geometry_by_ecc_info(this,
requirements->strength,
requirements->step_size);
/* use legacy bch geometry settings by default*/
if ((!use_minimun_ecc && mtd->oobsize < 1024) ||
!(requirements->strength > 0 && requirements->step_size > 0)) {
dev_dbg(this->dev, "use legacy bch geometry\n");
err = legacy_set_geometry(this);
if (!err)
return 0;
}
return 0;
/* for large oob nand */
if (mtd->oobsize > 1024) {
dev_dbg(this->dev, "use large oob bch geometry\n");
err = set_geometry_for_large_oob(this);
if (!err)
return 0;
}
/* otherwise use the minimum ecc nand chip required */
dev_dbg(this->dev, "use minimum ecc bch geometry\n");
err = set_geometry_by_ecc_info(this, requirements->strength,
requirements->step_size);
if (err)
dev_err(this->dev, "none of the bch geometry setting works\n");
return err;
}
/* Configures the geometry for BCH. */
@ -843,7 +1038,7 @@ static int gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len)
* we are passed in exec_op. Calculate the data length from it.
*/
if (this->bch)
return ALIGN_DOWN(raw_len, this->bch_geometry.ecc_chunk_size);
return ALIGN_DOWN(raw_len, this->bch_geometry.eccn_chunk_size);
else
return raw_len;
}
@ -1235,7 +1430,7 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first,
/* Read ECC bytes into our internal raw_buffer */
offset = nfc_geo->metadata_size * 8;
offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1);
offset += ((8 * nfc_geo->eccn_chunk_size) + eccbits) * (i + 1);
offset -= eccbits;
bitoffset = offset % 8;
eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
@ -1272,16 +1467,16 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first,
if (i == 0) {
/* The first block includes metadata */
flips = nand_check_erased_ecc_chunk(
buf + i * nfc_geo->ecc_chunk_size,
nfc_geo->ecc_chunk_size,
buf + i * nfc_geo->eccn_chunk_size,
nfc_geo->eccn_chunk_size,
eccbuf, eccbytes,
this->auxiliary_virt,
nfc_geo->metadata_size,
nfc_geo->ecc_strength);
} else {
flips = nand_check_erased_ecc_chunk(
buf + i * nfc_geo->ecc_chunk_size,
nfc_geo->ecc_chunk_size,
buf + i * nfc_geo->eccn_chunk_size,
nfc_geo->eccn_chunk_size,
eccbuf, eccbytes,
NULL, 0,
nfc_geo->ecc_strength);
@ -1310,20 +1505,21 @@ static void gpmi_bch_layout_std(struct gpmi_nand_data *this)
struct bch_geometry *geo = &this->bch_geometry;
unsigned int ecc_strength = geo->ecc_strength >> 1;
unsigned int gf_len = geo->gf_len;
unsigned int block_size = geo->ecc_chunk_size;
unsigned int block0_size = geo->ecc0_chunk_size;
unsigned int blockn_size = geo->eccn_chunk_size;
this->bch_flashlayout0 =
BF_BCH_FLASH0LAYOUT0_NBLOCKS(geo->ecc_chunk_count - 1) |
BF_BCH_FLASH0LAYOUT0_META_SIZE(geo->metadata_size) |
BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) |
BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) |
BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this);
BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block0_size, this);
this->bch_flashlayout1 =
BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(geo->page_size) |
BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) |
BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) |
BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this);
BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(blockn_size, this);
}
static int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf,
@ -1406,29 +1602,49 @@ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs,
}
}
/*
* if there is an ECC dedicate for meta:
* - need to add an extra ECC size when calculating col and page_size,
* if the meta size is NOT zero.
* - ecc0_chunk size need to set to the same size as other chunks,
* if the meta size is zero.
*/
meta = geo->metadata_size;
if (first) {
col = meta + (size + ecc_parity_size) * first;
if (geo->ecc_for_meta)
col = meta + ecc_parity_size
+ (size + ecc_parity_size) * first;
else
col = meta + (size + ecc_parity_size) * first;
meta = 0;
buf = buf + first * size;
}
ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
n = last - first + 1;
page_size = meta + (size + ecc_parity_size) * n;
if (geo->ecc_for_meta && meta)
page_size = meta + ecc_parity_size
+ (size + ecc_parity_size) * n;
else
page_size = meta + (size + ecc_parity_size) * n;
ecc_strength = geo->ecc_strength >> 1;
this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) |
this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(
(geo->ecc_for_meta ? n : n - 1)) |
BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) |
BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) |
BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) |
BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(geo->ecc_chunk_size, this);
BF_BCH_FLASH0LAYOUT0_DATA0_SIZE((geo->ecc_for_meta ?
0 : geo->ecc0_chunk_size), this);
this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) |
BF_BCH_FLASH0LAYOUT1_GF(geo->gf_len, this) |
BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->ecc_chunk_size, this);
BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->eccn_chunk_size, this);
this->bch = true;
@ -1597,7 +1813,7 @@ static int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
struct mtd_info *mtd = nand_to_mtd(chip);
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->ecc_chunk_size;
int eccsize = nfc_geo->eccn_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *tmp_buf = this->raw_buffer;
size_t src_bit_off;
@ -1682,7 +1898,7 @@ static int gpmi_ecc_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
struct mtd_info *mtd = nand_to_mtd(chip);
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->ecc_chunk_size;
int eccsize = nfc_geo->eccn_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *tmp_buf = this->raw_buffer;
uint8_t *oob = chip->oob_poi;
@ -2056,7 +2272,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
ecc->read_oob_raw = gpmi_ecc_read_oob_raw;
ecc->write_oob_raw = gpmi_ecc_write_oob_raw;
ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
ecc->size = bch_geo->ecc_chunk_size;
ecc->size = bch_geo->eccn_chunk_size;
ecc->strength = bch_geo->ecc_strength;
mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops);

View File

@ -30,9 +30,9 @@ struct resources {
* @page_size: The size, in bytes, of a physical page, including
* both data and OOB.
* @metadata_size: The size, in bytes, of the metadata.
* @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
* the first chunk in the page includes both data and
* metadata, so it's a bit larger than this value.
* @ecc0_chunk_size: The size, in bytes, of a first ECC chunk.
* @eccn_chunk_size: The size, in bytes, of a single ECC chunk after
* the first chunk in the page.
* @ecc_chunk_count: The number of ECC chunks in the page,
* @payload_size: The size, in bytes, of the payload buffer.
* @auxiliary_size: The size, in bytes, of the auxiliary buffer.
@ -42,19 +42,23 @@ struct resources {
* which the underlying physical block mark appears.
* @block_mark_bit_offset: The bit offset into the ECC-based page view at
* which the underlying physical block mark appears.
* @ecc_for_meta: The flag to indicate if there is a dedicate ecc
* for meta.
*/
struct bch_geometry {
unsigned int gf_len;
unsigned int ecc_strength;
unsigned int page_size;
unsigned int metadata_size;
unsigned int ecc_chunk_size;
unsigned int ecc0_chunk_size;
unsigned int eccn_chunk_size;
unsigned int ecc_chunk_count;
unsigned int payload_size;
unsigned int auxiliary_size;
unsigned int auxiliary_status_offset;
unsigned int block_mark_byte_offset;
unsigned int block_mark_bit_offset;
unsigned int ecc_for_meta; /* ECC for meta data */
};
/**

View File

@ -619,9 +619,9 @@ static int ebu_nand_probe(struct platform_device *pdev)
resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", cs);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname);
ebu_host->cs[cs].chipaddr = devm_ioremap_resource(dev, res);
ebu_host->cs[cs].nand_pa = res->start;
if (IS_ERR(ebu_host->cs[cs].chipaddr))
return PTR_ERR(ebu_host->cs[cs].chipaddr);
ebu_host->cs[cs].nand_pa = res->start;
ebu_host->clk = devm_clk_get(dev, NULL);
if (IS_ERR(ebu_host->clk))

View File

@ -595,8 +595,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
struct nand_chip *chip = mtd_to_nand(mtd);
struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
if (prv->clk)
clk_disable_unprepare(prv->clk);
clk_disable_unprepare(prv->clk);
if (prv->csreg)
iounmap(prv->csreg);

View File

@ -17,7 +17,7 @@
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include "mtk_ecc.h"
#include <linux/mtd/nand-ecc-mtk.h>
/* NAND controller register definition */
#define NFI_CNFG (0x00)

View File

@ -4502,11 +4502,13 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
len = instr->len;
while (len) {
loff_t ofs = (loff_t)page << chip->page_shift;
/* Check if we have a bad block, we do not erase bad blocks! */
if (nand_block_checkbad(chip, ((loff_t) page) <<
chip->page_shift, allowbbt)) {
pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
__func__, page);
pr_warn("%s: attempt to erase a bad block at 0x%08llx\n",
__func__, (unsigned long long)ofs);
ret = -EIO;
goto erase_exit;
}
@ -4524,8 +4526,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
if (ret) {
pr_debug("%s: failed erase, page 0x%08x\n",
__func__, page);
instr->fail_addr =
((loff_t)page << chip->page_shift);
instr->fail_addr = ofs;
goto erase_exit;
}

View File

@ -29,6 +29,9 @@ struct nand_flash_dev nand_flash_ids[] = {
{"TC58NVG0S3E 1G 3.3V 8-bit",
{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
{"TC58NVG0S3HTA00 1G 3.3V 8-bit",
{ .id = {0x98, 0xf1, 0x80, 0x15} },
SZ_2K, SZ_128, SZ_128K, 0, 4, 128, NAND_ECC_INFO(8, SZ_512), },
{"TC58NVG2S0F 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
@ -58,6 +61,9 @@ struct nand_flash_dev nand_flash_ids[] = {
{"TH58NVG2S3HBAI4 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x91, 0x15, 0x76} },
SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) },
{"TH58NVG3S0HBAI4 8G 3.3V 8-bit",
{ .id = {0x98, 0xd3, 0x91, 0x26, 0x76} },
SZ_4K, SZ_1K, SZ_256K, 0, 5, 256, NAND_ECC_INFO(8, SZ_512)},
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),

View File

@ -287,8 +287,10 @@ static int toshiba_nand_init(struct nand_chip *chip)
if (!strncmp("TC58NVG0S3E", chip->parameters.model,
sizeof("TC58NVG0S3E") - 1))
tc58nvg0s3e_init(chip);
if (!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
sizeof("TH58NVG2S3HBAI4") - 1))
if ((!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
sizeof("TH58NVG2S3HBAI4") - 1)) ||
(!strncmp("TH58NVG3S0HBAI4", chip->parameters.model,
sizeof("TH58NVG3S0HBAI4") - 1)))
th58nvg2s3hbai4_init(chip);
return 0;

View File

@ -548,6 +548,7 @@ static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
#ifdef CONFIG_OF
static const struct of_device_id elm_of_match[] = {
{ .compatible = "ti,am3352-elm" },
{ .compatible = "ti,am64-elm" },
{},
};
MODULE_DEVICE_TABLE(of, elm_of_match);

View File

@ -16,6 +16,7 @@
#include <linux/mtd/rawnand.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#define COMMAND_REG 0x00
@ -216,8 +217,7 @@ struct rnandc {
struct nand_controller controller;
struct device *dev;
void __iomem *regs;
struct clk *hclk;
struct clk *eclk;
unsigned long ext_clk_rate;
unsigned long assigned_cs;
struct list_head chips;
struct nand_chip *selected_chip;
@ -891,7 +891,7 @@ static int rnandc_setup_interface(struct nand_chip *chip, int chipnr,
{
struct rnand_chip *rnand = to_rnand(chip);
struct rnandc *rnandc = to_rnandc(chip->controller);
unsigned int period_ns = 1000000000 / clk_get_rate(rnandc->eclk);
unsigned int period_ns = 1000000000 / rnandc->ext_clk_rate;
const struct nand_sdr_timings *sdr;
unsigned int cyc, cle, ale, bef_dly, ca_to_data;
@ -1319,6 +1319,7 @@ cleanup_chips:
static int rnandc_probe(struct platform_device *pdev)
{
struct rnandc *rnandc;
struct clk *eclk;
int irq, ret;
rnandc = devm_kzalloc(&pdev->dev, sizeof(*rnandc), GFP_KERNEL);
@ -1335,29 +1336,26 @@ static int rnandc_probe(struct platform_device *pdev)
if (IS_ERR(rnandc->regs))
return PTR_ERR(rnandc->regs);
/* APB clock */
rnandc->hclk = devm_clk_get(&pdev->dev, "hclk");
if (IS_ERR(rnandc->hclk))
return PTR_ERR(rnandc->hclk);
/* External NAND bus clock */
rnandc->eclk = devm_clk_get(&pdev->dev, "eclk");
if (IS_ERR(rnandc->eclk))
return PTR_ERR(rnandc->eclk);
ret = clk_prepare_enable(rnandc->hclk);
if (ret)
devm_pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
return ret;
ret = clk_prepare_enable(rnandc->eclk);
if (ret)
goto disable_hclk;
/* The external NAND bus clock rate is needed for computing timings */
eclk = clk_get(&pdev->dev, "eclk");
if (IS_ERR(eclk)) {
ret = PTR_ERR(eclk);
goto dis_runtime_pm;
}
rnandc->ext_clk_rate = clk_get_rate(eclk);
clk_put(eclk);
rnandc_dis_interrupts(rnandc);
irq = platform_get_irq_optional(pdev, 0);
if (irq == -EPROBE_DEFER) {
ret = irq;
goto disable_eclk;
goto dis_runtime_pm;
} else if (irq < 0) {
dev_info(&pdev->dev, "No IRQ found, fallback to polling\n");
rnandc->use_polling = true;
@ -1365,12 +1363,12 @@ static int rnandc_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, irq, rnandc_irq_handler, 0,
"renesas-nand-controller", rnandc);
if (ret < 0)
goto disable_eclk;
goto dis_runtime_pm;
}
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
goto disable_eclk;
goto dis_runtime_pm;
rnandc_clear_fifo(rnandc);
@ -1378,14 +1376,12 @@ static int rnandc_probe(struct platform_device *pdev)
ret = rnandc_chips_init(rnandc);
if (ret)
goto disable_eclk;
goto dis_runtime_pm;
return 0;
disable_eclk:
clk_disable_unprepare(rnandc->eclk);
disable_hclk:
clk_disable_unprepare(rnandc->hclk);
dis_runtime_pm:
pm_runtime_put(&pdev->dev);
return ret;
}
@ -1396,8 +1392,7 @@ static int rnandc_remove(struct platform_device *pdev)
rnandc_chips_cleanup(rnandc);
clk_disable_unprepare(rnandc->eclk);
clk_disable_unprepare(rnandc->hclk);
pm_runtime_put(&pdev->dev);
return 0;
}

View File

@ -911,8 +911,7 @@ static int rk_nfc_enable_clks(struct device *dev, struct rk_nfc *nfc)
ret = clk_prepare_enable(nfc->ahb_clk);
if (ret) {
dev_err(dev, "failed to enable ahb clk\n");
if (!IS_ERR(nfc->nfc_clk))
clk_disable_unprepare(nfc->nfc_clk);
clk_disable_unprepare(nfc->nfc_clk);
return ret;
}
@ -921,8 +920,7 @@ static int rk_nfc_enable_clks(struct device *dev, struct rk_nfc *nfc)
static void rk_nfc_disable_clks(struct rk_nfc *nfc)
{
if (!IS_ERR(nfc->nfc_clk))
clk_disable_unprepare(nfc->nfc_clk);
clk_disable_unprepare(nfc->nfc_clk);
clk_disable_unprepare(nfc->ahb_clk);
}

View File

@ -390,6 +390,9 @@ static int tmio_probe(struct platform_device *dev)
if (data == NULL)
dev_warn(&dev->dev, "NULL platform data!\n");
if (!ccr || !fcr)
return -EINVAL;
tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL);
if (!tmio)
return -ENOMEM;

View File

@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o
spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o

View File

@ -933,6 +933,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
&paragon_spinand_manufacturer,
&toshiba_spinand_manufacturer,
&winbond_spinand_manufacturer,
&xtx_spinand_manufacturer,
};
static int spinand_manufacturer_match(struct spinand_device *spinand,

View File

@ -39,6 +39,22 @@ static SPINAND_OP_VARIANTS(read_cache_variants_f,
SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0));
static SPINAND_OP_VARIANTS(read_cache_variants_1gq5,
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
static SPINAND_OP_VARIANTS(read_cache_variants_2gq5,
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
SPINAND_PROG_LOAD(true, 0, NULL, 0));
@ -325,6 +341,36 @@ static const struct spinand_info gigadevice_spinand_table[] = {
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GQ4RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc1),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GQ4UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd2),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GQ4RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc2),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GQ4UFxxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
@ -339,12 +385,122 @@ static const struct spinand_info gigadevice_spinand_table[] = {
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GQ5RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x41),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GQ5UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GQ5RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x42),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F4GQ6UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x55),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F4GQ6RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x45),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq5xexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GM7UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x91),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GM7RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GM7UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F2GM7RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x82),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F4GM8UExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x95),
NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F4GM8RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x85),
NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
};
static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {

129
drivers/mtd/nand/spi/xtx.c Normal file
View File

@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Author:
* Felix Matouschek <felix@matouschek.org>
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
#define SPINAND_MFR_XTX 0x0B
#define XT26G0XA_STATUS_ECC_MASK GENMASK(5, 2)
#define XT26G0XA_STATUS_ECC_NO_DETECTED (0 << 2)
#define XT26G0XA_STATUS_ECC_8_CORRECTED (3 << 4)
#define XT26G0XA_STATUS_ECC_UNCOR_ERROR (2 << 4)
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
SPINAND_PROG_LOAD(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
SPINAND_PROG_LOAD(false, 0, NULL, 0));
static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
region->offset = 48;
region->length = 16;
return 0;
}
static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
region->offset = 1;
region->length = 47;
return 0;
}
static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = {
.ecc = xt26g0xa_ooblayout_ecc,
.free = xt26g0xa_ooblayout_free,
};
static int xt26g0xa_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
status = status & XT26G0XA_STATUS_ECC_MASK;
switch (status) {
case XT26G0XA_STATUS_ECC_NO_DETECTED:
return 0;
case XT26G0XA_STATUS_ECC_8_CORRECTED:
return 8;
case XT26G0XA_STATUS_ECC_UNCOR_ERROR:
return -EBADMSG;
default:
break;
}
/* At this point values greater than (2 << 4) are invalid */
if (status > XT26G0XA_STATUS_ECC_UNCOR_ERROR)
return -EINVAL;
/* (1 << 2) through (7 << 2) are 1-7 corrected errors */
return status >> 2;
}
static const struct spinand_info xtx_spinand_table[] = {
SPINAND_INFO("XT26G01A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g0xa_ooblayout,
xt26g0xa_ecc_get_status)),
SPINAND_INFO("XT26G02A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2),
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g0xa_ooblayout,
xt26g0xa_ecc_get_status)),
SPINAND_INFO("XT26G04A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3),
NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g0xa_ooblayout,
xt26g0xa_ecc_get_status)),
};
static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
};
const struct spinand_manufacturer xtx_spinand_manufacturer = {
.id = SPINAND_MFR_XTX,
.name = "XTX",
.chips = xtx_spinand_table,
.nchips = ARRAY_SIZE(xtx_spinand_table),
.ops = &xtx_spinand_manuf_ops,
};

View File

@ -266,6 +266,7 @@ extern const struct spinand_manufacturer micron_spinand_manufacturer;
extern const struct spinand_manufacturer paragon_spinand_manufacturer;
extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
extern const struct spinand_manufacturer winbond_spinand_manufacturer;
extern const struct spinand_manufacturer xtx_spinand_manufacturer;
/**
* struct spinand_op_variants - SPI NAND operation variants