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:
commit
2c51d0d880
@ -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>;
|
||||
};
|
@ -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>;
|
||||
};
|
||||
|
72
Documentation/devicetree/bindings/mtd/ti,elm.yaml
Normal file
72
Documentation/devicetree/bindings/mtd/ti,elm.yaml
Normal 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>;
|
||||
};
|
@ -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
|
||||
|
@ -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/
|
||||
|
@ -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);
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -933,6 +933,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
|
||||
¶gon_spinand_manufacturer,
|
||||
&toshiba_spinand_manufacturer,
|
||||
&winbond_spinand_manufacturer,
|
||||
&xtx_spinand_manufacturer,
|
||||
};
|
||||
|
||||
static int spinand_manufacturer_match(struct spinand_device *spinand,
|
||||
|
@ -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
129
drivers/mtd/nand/spi/xtx.c
Normal 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,
|
||||
};
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user