NAND core changes:

* Check the data only read pattern only once
 * Prepare the late addition of supported operation checks
 * Support for sequential cache reads
 * Fix nand_chip kdoc
 
 Raw NAND changes:
 * Fsl_elbc: Propagate HW ECC settings to HW
 * Marvell: Add missing layouts
 * Pasemi: Don't use static data to track per-device state
 * Sunxi:
   - Fix the size of the last OOB region
   - Remove an unnecessary check
   - Remove an unnecessary check
   - Clean up chips after failed init
   - Precompute the ECC_CTL register value
   - Embed sunxi_nand_hw_ecc by value
   - Update OOB layout to match hardware
 * tmio_nand: Remove driver
 * vf610_nfc: Use regular comments for functions
 
 SPI-NAND changes:
 * Add support for AllianceMemory AS5F34G04SND
 * Macronix: use scratch buffer for DMA operation
 
 NAND ECC changes:
 * Mediatek:
   - Add ECC support fot MT7986 IC
   - Add compatible for MT7986
   - dt-bindings: Split ECC engine with rawnand controller
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmP3L4MACgkQJWrqGEe9
 VoSVKgf+PUOBlR6U/QaNlzavYsTOz1Hgc9MjuC32CtStED8/uKfEG9odBRw1Fp+I
 fNpgVnaWrU7KpW9yzsMTLQy//6lI/Hjn4KUMqKcsBmN3dOvzca60YrbehWLlrARb
 exRDFvwuw6qZ0jRYMsKC9jsKhvU69TXnAWCdC+TSWRwfXci5dfjm3HxMNpRvT+PQ
 q3sWqFMGj+omLOr/R+sBzhSV0WU1FpQsG9NB6I0VbFiJGy9YMMaI2tr/TKJyYeqM
 CM0T3tccjKAajJ9i9qKIPZnRYWQYx/FyJ5Uyg6DRxjeoVjsFhGom1pQbH/2eHVs6
 iND3n1yQCsnPaR/D7yRZgiTERQzqdw==
 =psZR
 -----END PGP SIGNATURE-----

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

NAND core changes:
* Check the data only read pattern only once
* Prepare the late addition of supported operation checks
* Support for sequential cache reads
* Fix nand_chip kdoc

Raw NAND changes:
* Fsl_elbc: Propagate HW ECC settings to HW
* Marvell: Add missing layouts
* Pasemi: Don't use static data to track per-device state
* Sunxi:
  - Fix the size of the last OOB region
  - Remove an unnecessary check
  - Remove an unnecessary check
  - Clean up chips after failed init
  - Precompute the ECC_CTL register value
  - Embed sunxi_nand_hw_ecc by value
  - Update OOB layout to match hardware
* tmio_nand: Remove driver
* vf610_nfc: Use regular comments for functions

SPI-NAND changes:
* Add support for AllianceMemory AS5F34G04SND
* Macronix: use scratch buffer for DMA operation

NAND ECC changes:
* Mediatek:
  - Add ECC support fot MT7986 IC
  - Add compatible for MT7986
  - dt-bindings: Split ECC engine with rawnand controller
This commit is contained in:
Miquel Raynal 2023-02-23 10:28:29 +01:00
commit f4440abc08
22 changed files with 664 additions and 841 deletions

View File

@ -0,0 +1,155 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/mediatek,mtk-nfc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek(MTK) SoCs raw NAND FLASH controller (NFC)
maintainers:
- Xiangsheng Hou <xiangsheng.hou@mediatek.com>
properties:
compatible:
enum:
- mediatek,mt2701-nfc
- mediatek,mt2712-nfc
- mediatek,mt7622-nfc
reg:
items:
- description: Base physical address and size of NFI.
interrupts:
items:
- description: NFI interrupt
clocks:
items:
- description: clock used for the controller
- description: clock used for the pad
clock-names:
items:
- const: nfi_clk
- const: pad_clk
ecc-engine:
description: device-tree node of the required ECC engine.
$ref: /schemas/types.yaml#/definitions/phandle
patternProperties:
"^nand@[a-f0-9]$":
$ref: nand-chip.yaml#
unevaluatedProperties: false
properties:
reg:
maximum: 1
nand-on-flash-bbt: true
nand-ecc-mode:
const: hw
allOf:
- $ref: nand-controller.yaml#
- if:
properties:
compatible:
contains:
const: mediatek,mt2701-nfc
then:
patternProperties:
"^nand@[a-f0-9]$":
properties:
nand-ecc-step-size:
enum: [ 512, 1024 ]
nand-ecc-strength:
enum: [4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
40, 44, 48, 52, 56, 60]
- if:
properties:
compatible:
contains:
const: mediatek,mt2712-nfc
then:
patternProperties:
"^nand@[a-f0-9]$":
properties:
nand-ecc-step-size:
enum: [ 512, 1024 ]
nand-ecc-strength:
enum: [4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
40, 44, 48, 52, 56, 60, 68, 72, 80]
- if:
properties:
compatible:
contains:
const: mediatek,mt7622-nfc
then:
patternProperties:
"^nand@[a-f0-9]$":
properties:
nand-ecc-step-size:
const: 512
nand-ecc-strength:
enum: [4, 6, 8, 10, 12]
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- ecc-engine
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/mt2701-clk.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
nand-controller@1100d000 {
compatible = "mediatek,mt2701-nfc";
reg = <0 0x1100d000 0 0x1000>;
interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_NFI>,
<&pericfg CLK_PERI_NFI_PAD>;
clock-names = "nfi_clk", "pad_clk";
ecc-engine = <&bch>;
#address-cells = <1>;
#size-cells = <0>;
nand@0 {
reg = <0>;
nand-on-flash-bbt;
nand-ecc-mode = "hw";
nand-ecc-step-size = <1024>;
nand-ecc-strength = <24>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
preloader@0 {
label = "pl";
read-only;
reg = <0x0 0x400000>;
};
android@400000 {
label = "android";
reg = <0x400000 0x12c00000>;
};
};
};
};
};

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/mediatek,nand-ecc-engine.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek(MTK) SoCs NAND ECC engine
maintainers:
- Xiangsheng Hou <xiangsheng.hou@mediatek.com>
description: |
MTK NAND ECC engine can cowork with MTK raw NAND and SPI NAND controller.
properties:
compatible:
enum:
- mediatek,mt2701-ecc
- mediatek,mt2712-ecc
- mediatek,mt7622-ecc
- mediatek,mt7986-ecc
reg:
items:
- description: Base physical address and size of ECC.
interrupts:
items:
- description: ECC interrupt
clocks:
maxItems: 1
clock-names:
const: nfiecc_clk
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/mt2701-clk.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
bch: ecc@1100e000 {
compatible = "mediatek,mt2701-ecc";
reg = <0 0x1100e000 0 0x1000>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_NFI_ECC>;
clock-names = "nfiecc_clk";
};
};

View File

@ -1,176 +0,0 @@
MTK SoCs NAND FLASH controller (NFC) DT binding
This file documents the device tree bindings for MTK SoCs NAND controllers.
The functional split of the controller requires two drivers to operate:
the nand controller interface driver and the ECC engine driver.
The hardware description for both devices must be captured as device
tree nodes.
1) NFC NAND Controller Interface (NFI):
=======================================
The first part of NFC is NAND Controller Interface (NFI) HW.
Required NFI properties:
- compatible: Should be one of
"mediatek,mt2701-nfc",
"mediatek,mt2712-nfc",
"mediatek,mt7622-nfc".
- reg: Base physical address and size of NFI.
- interrupts: Interrupts of NFI.
- clocks: NFI required clocks.
- clock-names: NFI clocks internal name.
- ecc-engine: Required ECC Engine node.
- #address-cells: NAND chip index, should be 1.
- #size-cells: Should be 0.
Example:
nandc: nfi@1100d000 {
compatible = "mediatek,mt2701-nfc";
reg = <0 0x1100d000 0 0x1000>;
interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_NFI>,
<&pericfg CLK_PERI_NFI_PAD>;
clock-names = "nfi_clk", "pad_clk";
ecc-engine = <&bch>;
#address-cells = <1>;
#size-cells = <0>;
};
Platform related properties, should be set in {platform_name}.dts:
- children nodes: NAND chips.
Children nodes properties:
- reg: Chip Select Signal, default 0.
Set as reg = <0>, <1> when need 2 CS.
Optional:
- nand-on-flash-bbt: Store BBT on NAND Flash.
- nand-ecc-mode: the NAND ecc mode (check driver for supported modes)
- nand-ecc-step-size: Number of data bytes covered by a single ECC step.
valid values:
512 and 1024 on mt2701 and mt2712.
512 only on mt7622.
1024 is recommended for large page NANDs.
- nand-ecc-strength: Number of bits to correct per ECC step.
The valid values that each controller supports:
mt2701: 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28,
32, 36, 40, 44, 48, 52, 56, 60.
mt2712: 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28,
32, 36, 40, 44, 48, 52, 56, 60, 68, 72, 80.
mt7622: 4, 6, 8, 10, 12, 14, 16.
The strength should be calculated as follows:
E = (S - F) * 8 / B
S = O / (P / Q)
E : nand-ecc-strength.
S : spare size per sector.
F : FDM size, should be in the range [1,8].
It is used to store free oob data.
O : oob size.
P : page size.
Q : nand-ecc-step-size.
B : number of parity bits needed to correct
1 bitflip.
According to MTK NAND controller design,
this number depends on max ecc step size
that MTK NAND controller supports.
If max ecc step size supported is 1024,
then it should be always 14. And if max
ecc step size is 512, then it should be
always 13.
If the result does not match any one of the listed
choices above, please select the smaller valid value from
the list.
(otherwise the driver will do the adjustment at runtime)
- pinctrl-names: Default NAND pin GPIO setting name.
- pinctrl-0: GPIO setting node.
Example:
&pio {
nand_pins_default: nanddefault {
pins_dat {
pinmux = <MT2701_PIN_111_MSDC0_DAT7__FUNC_NLD7>,
<MT2701_PIN_112_MSDC0_DAT6__FUNC_NLD6>,
<MT2701_PIN_114_MSDC0_DAT4__FUNC_NLD4>,
<MT2701_PIN_118_MSDC0_DAT3__FUNC_NLD3>,
<MT2701_PIN_121_MSDC0_DAT0__FUNC_NLD0>,
<MT2701_PIN_120_MSDC0_DAT1__FUNC_NLD1>,
<MT2701_PIN_113_MSDC0_DAT5__FUNC_NLD5>,
<MT2701_PIN_115_MSDC0_RSTB__FUNC_NLD8>,
<MT2701_PIN_119_MSDC0_DAT2__FUNC_NLD2>;
input-enable;
drive-strength = <MTK_DRIVE_8mA>;
bias-pull-up;
};
pins_we {
pinmux = <MT2701_PIN_117_MSDC0_CLK__FUNC_NWEB>;
drive-strength = <MTK_DRIVE_8mA>;
bias-pull-up = <MTK_PUPD_SET_R1R0_10>;
};
pins_ale {
pinmux = <MT2701_PIN_116_MSDC0_CMD__FUNC_NALE>;
drive-strength = <MTK_DRIVE_8mA>;
bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
};
};
};
&nandc {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&nand_pins_default>;
nand@0 {
reg = <0>;
nand-on-flash-bbt;
nand-ecc-mode = "hw";
nand-ecc-strength = <24>;
nand-ecc-step-size = <1024>;
};
};
NAND chip optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
Example:
nand@0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
preloader@0 {
label = "pl";
read-only;
reg = <0x00000000 0x00400000>;
};
android@00400000 {
label = "android";
reg = <0x00400000 0x12c00000>;
};
};
};
2) ECC Engine:
==============
Required BCH properties:
- compatible: Should be one of
"mediatek,mt2701-ecc",
"mediatek,mt2712-ecc",
"mediatek,mt7622-ecc".
- reg: Base physical address and size of ECC.
- interrupts: Interrupts of ECC.
- clocks: ECC required clocks.
- clock-names: ECC clocks internal name.
Example:
bch: ecc@1100e000 {
compatible = "mediatek,mt2701-ecc";
reg = <0 0x1100e000 0 0x1000>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_NFI_ECC>;
clock-names = "nfiecc_clk";
};

View File

@ -13213,7 +13213,7 @@ F: drivers/phy/ralink/phy-mt7621-pci.c
MEDIATEK NAND CONTROLLER DRIVER MEDIATEK NAND CONTROLLER DRIVER
L: linux-mtd@lists.infradead.org L: linux-mtd@lists.infradead.org
S: Orphan S: Orphan
F: Documentation/devicetree/bindings/mtd/mtk-nand.txt F: Documentation/devicetree/bindings/mtd/mediatek,mtk-nfc.yaml
F: drivers/mtd/nand/raw/mtk_* F: drivers/mtd/nand/raw/mtk_*
MEDIATEK PMIC LED DRIVER MEDIATEK PMIC LED DRIVER

View File

@ -40,6 +40,10 @@
#define ECC_IDLE_REG(op) ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE) #define ECC_IDLE_REG(op) ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE)
#define ECC_CTL_REG(op) ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON) #define ECC_CTL_REG(op) ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON)
#define ECC_ERRMASK_MT7622 GENMASK(4, 0)
#define ECC_ERRMASK_MT2701 GENMASK(5, 0)
#define ECC_ERRMASK_MT2712 GENMASK(6, 0)
struct mtk_ecc_caps { struct mtk_ecc_caps {
u32 err_mask; u32 err_mask;
u32 err_shift; u32 err_shift;
@ -79,6 +83,10 @@ static const u8 ecc_strength_mt7622[] = {
4, 6, 8, 10, 12 4, 6, 8, 10, 12
}; };
static const u8 ecc_strength_mt7986[] = {
4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24
};
enum mtk_ecc_regs { enum mtk_ecc_regs {
ECC_ENCPAR00, ECC_ENCPAR00,
ECC_ENCIRQ_EN, ECC_ENCIRQ_EN,
@ -451,7 +459,7 @@ unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc)
EXPORT_SYMBOL(mtk_ecc_get_parity_bits); EXPORT_SYMBOL(mtk_ecc_get_parity_bits);
static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = { static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
.err_mask = 0x3f, .err_mask = ECC_ERRMASK_MT2701,
.err_shift = 8, .err_shift = 8,
.ecc_strength = ecc_strength_mt2701, .ecc_strength = ecc_strength_mt2701,
.ecc_regs = mt2701_ecc_regs, .ecc_regs = mt2701_ecc_regs,
@ -462,7 +470,7 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
}; };
static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = { static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
.err_mask = 0x7f, .err_mask = ECC_ERRMASK_MT2712,
.err_shift = 8, .err_shift = 8,
.ecc_strength = ecc_strength_mt2712, .ecc_strength = ecc_strength_mt2712,
.ecc_regs = mt2712_ecc_regs, .ecc_regs = mt2712_ecc_regs,
@ -473,7 +481,7 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
}; };
static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = { static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = {
.err_mask = 0x1f, .err_mask = ECC_ERRMASK_MT7622,
.err_shift = 5, .err_shift = 5,
.ecc_strength = ecc_strength_mt7622, .ecc_strength = ecc_strength_mt7622,
.ecc_regs = mt7622_ecc_regs, .ecc_regs = mt7622_ecc_regs,
@ -483,6 +491,17 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = {
.pg_irq_sel = 0, .pg_irq_sel = 0,
}; };
static const struct mtk_ecc_caps mtk_ecc_caps_mt7986 = {
.err_mask = ECC_ERRMASK_MT7622,
.err_shift = 8,
.ecc_strength = ecc_strength_mt7986,
.ecc_regs = mt2712_ecc_regs,
.num_ecc_strength = 11,
.ecc_mode_shift = 5,
.parity_bits = 14,
.pg_irq_sel = 1,
};
static const struct of_device_id mtk_ecc_dt_match[] = { static const struct of_device_id mtk_ecc_dt_match[] = {
{ {
.compatible = "mediatek,mt2701-ecc", .compatible = "mediatek,mt2701-ecc",
@ -493,6 +512,9 @@ static const struct of_device_id mtk_ecc_dt_match[] = {
}, { }, {
.compatible = "mediatek,mt7622-ecc", .compatible = "mediatek,mt7622-ecc",
.data = &mtk_ecc_caps_mt7622, .data = &mtk_ecc_caps_mt7622,
}, {
.compatible = "mediatek,mt7986-ecc",
.data = &mtk_ecc_caps_mt7986,
}, },
{}, {},
}; };

View File

@ -193,13 +193,6 @@ config MTD_NAND_PASEMI
Enables support for NAND Flash interface on PA Semi PWRficient Enables support for NAND Flash interface on PA Semi PWRficient
based boards based boards
config MTD_NAND_TMIO
tristate "Toshiba Mobile IO NAND controller"
depends on MFD_TMIO
help
Support for NAND flash connected to a Toshiba Mobile IO
Controller in some PDAs, including the Sharp SL6000x.
source "drivers/mtd/nand/raw/brcmnand/Kconfig" source "drivers/mtd/nand/raw/brcmnand/Kconfig"
config MTD_NAND_BCM47XXNFLASH config MTD_NAND_BCM47XXNFLASH

View File

@ -23,7 +23,6 @@ omap2_nand-objs := omap2.o
obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o
obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o
obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o
obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o

View File

@ -725,6 +725,7 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip)
struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
unsigned int al; unsigned int al;
u32 br;
/* /*
* if ECC was not chosen in DT, decide whether to use HW or SW ECC from * if ECC was not chosen in DT, decide whether to use HW or SW ECC from
@ -764,6 +765,13 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip)
return -EINVAL; return -EINVAL;
} }
/* enable/disable HW ECC checking and generating based on if HW ECC was chosen */
br = in_be32(&lbc->bank[priv->bank].br) & ~BR_DECC;
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
out_be32(&lbc->bank[priv->bank].br, br | BR_DECC_CHK_GEN);
else
out_be32(&lbc->bank[priv->bank].br, br | BR_DECC_OFF);
/* calculate FMR Address Length field */ /* calculate FMR Address Length field */
al = 0; al = 0;
if (chip->pagemask & 0xffff0000) if (chip->pagemask & 0xffff0000)

View File

@ -288,10 +288,17 @@ static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = {
MARVELL_LAYOUT( 2048, 512, 1, 1, 1, 2048, 40, 24, 0, 0, 0), MARVELL_LAYOUT( 2048, 512, 1, 1, 1, 2048, 40, 24, 0, 0, 0),
MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0), MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0),
MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,32, 30), MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,32, 30),
MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,64, 30),
MARVELL_LAYOUT( 2048, 512, 12, 3, 2, 704, 0, 30,640, 0, 30),
MARVELL_LAYOUT( 2048, 512, 16, 5, 4, 512, 0, 30, 0, 32, 30),
MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0), MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0),
MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30), MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30),
MARVELL_LAYOUT( 4096, 512, 12, 6, 5, 704, 0, 30,576, 32, 30),
MARVELL_LAYOUT( 4096, 512, 16, 9, 8, 512, 0, 30, 0, 32, 30),
MARVELL_LAYOUT( 8192, 512, 4, 4, 4, 2048, 0, 30, 0, 0, 0), MARVELL_LAYOUT( 8192, 512, 4, 4, 4, 2048, 0, 30, 0, 0, 0),
MARVELL_LAYOUT( 8192, 512, 8, 9, 8, 1024, 0, 30, 0, 160, 30), MARVELL_LAYOUT( 8192, 512, 8, 9, 8, 1024, 0, 30, 0, 160, 30),
MARVELL_LAYOUT( 8192, 512, 12, 12, 11, 704, 0, 30,448, 64, 30),
MARVELL_LAYOUT( 8192, 512, 16, 17, 16, 512, 0, 30, 0, 32, 30),
}; };
/** /**

View File

@ -1208,6 +1208,73 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
return nand_exec_op(chip, &op); return nand_exec_op(chip, &op);
} }
static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page,
unsigned int offset_in_page, void *buf,
unsigned int len, bool check_only)
{
const struct nand_interface_config *conf =
nand_get_interface_config(chip);
u8 addrs[5];
struct nand_op_instr start_instrs[] = {
NAND_OP_CMD(NAND_CMD_READ0, 0),
NAND_OP_ADDR(4, addrs, 0),
NAND_OP_CMD(NAND_CMD_READSTART, NAND_COMMON_TIMING_NS(conf, tWB_max)),
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), 0),
NAND_OP_CMD(NAND_CMD_READCACHESEQ, NAND_COMMON_TIMING_NS(conf, tWB_max)),
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
NAND_COMMON_TIMING_NS(conf, tRR_min)),
NAND_OP_DATA_IN(len, buf, 0),
};
struct nand_op_instr cont_instrs[] = {
NAND_OP_CMD(page == chip->cont_read.last_page ?
NAND_CMD_READCACHEEND : NAND_CMD_READCACHESEQ,
NAND_COMMON_TIMING_NS(conf, tWB_max)),
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
NAND_COMMON_TIMING_NS(conf, tRR_min)),
NAND_OP_DATA_IN(len, buf, 0),
};
struct nand_operation start_op = NAND_OPERATION(chip->cur_cs, start_instrs);
struct nand_operation cont_op = NAND_OPERATION(chip->cur_cs, cont_instrs);
int ret;
if (!len) {
start_op.ninstrs--;
cont_op.ninstrs--;
}
ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
if (ret < 0)
return ret;
addrs[2] = page;
addrs[3] = page >> 8;
if (chip->options & NAND_ROW_ADDR_3) {
addrs[4] = page >> 16;
start_instrs[1].ctx.addr.naddrs++;
}
/* Check if cache reads are supported */
if (check_only) {
if (nand_check_op(chip, &start_op) || nand_check_op(chip, &cont_op))
return -EOPNOTSUPP;
return 0;
}
if (page == chip->cont_read.first_page)
return nand_exec_op(chip, &start_op);
else
return nand_exec_op(chip, &cont_op);
}
static bool rawnand_cont_read_ongoing(struct nand_chip *chip, unsigned int page)
{
return chip->cont_read.ongoing &&
page >= chip->cont_read.first_page &&
page <= chip->cont_read.last_page;
}
/** /**
* nand_read_page_op - Do a READ PAGE operation * nand_read_page_op - Do a READ PAGE operation
* @chip: The NAND chip * @chip: The NAND chip
@ -1233,10 +1300,16 @@ int nand_read_page_op(struct nand_chip *chip, unsigned int page,
return -EINVAL; return -EINVAL;
if (nand_has_exec_op(chip)) { if (nand_has_exec_op(chip)) {
if (mtd->writesize > 512) if (mtd->writesize > 512) {
return nand_lp_exec_read_page_op(chip, page, if (rawnand_cont_read_ongoing(chip, page))
offset_in_page, buf, return nand_lp_exec_cont_read_page_op(chip, page,
len); offset_in_page,
buf, len, false);
else
return nand_lp_exec_read_page_op(chip, page,
offset_in_page, buf,
len);
}
return nand_sp_exec_read_page_op(chip, page, offset_in_page, return nand_sp_exec_read_page_op(chip, page, offset_in_page,
buf, len); buf, len);
@ -3353,6 +3426,27 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
return NULL; return NULL;
} }
static void rawnand_enable_cont_reads(struct nand_chip *chip, unsigned int page,
u32 readlen, int col)
{
struct mtd_info *mtd = nand_to_mtd(chip);
if (!chip->controller->supported_op.cont_read)
return;
if ((col && col + readlen < (3 * mtd->writesize)) ||
(!col && readlen < (2 * mtd->writesize))) {
chip->cont_read.ongoing = false;
return;
}
chip->cont_read.ongoing = true;
chip->cont_read.first_page = page;
if (col)
chip->cont_read.first_page++;
chip->cont_read.last_page = page + ((readlen >> chip->page_shift) & chip->pagemask);
}
/** /**
* nand_setup_read_retry - [INTERN] Set the READ RETRY mode * nand_setup_read_retry - [INTERN] Set the READ RETRY mode
* @chip: NAND chip object * @chip: NAND chip object
@ -3426,6 +3520,8 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
oob = ops->oobbuf; oob = ops->oobbuf;
oob_required = oob ? 1 : 0; oob_required = oob ? 1 : 0;
rawnand_enable_cont_reads(chip, page, readlen, col);
while (1) { while (1) {
struct mtd_ecc_stats ecc_stats = mtd->ecc_stats; struct mtd_ecc_stats ecc_stats = mtd->ecc_stats;
@ -4991,6 +5087,47 @@ nand_manufacturer_name(const struct nand_manufacturer_desc *manufacturer_desc)
return manufacturer_desc ? manufacturer_desc->name : "Unknown"; return manufacturer_desc ? manufacturer_desc->name : "Unknown";
} }
static void rawnand_check_data_only_read_support(struct nand_chip *chip)
{
/* Use an arbitrary size for the check */
if (!nand_read_data_op(chip, NULL, SZ_512, true, true))
chip->controller->supported_op.data_only_read = 1;
}
static void rawnand_early_check_supported_ops(struct nand_chip *chip)
{
/* The supported_op fields should not be set by individual drivers */
WARN_ON_ONCE(chip->controller->supported_op.data_only_read);
if (!nand_has_exec_op(chip))
return;
rawnand_check_data_only_read_support(chip);
}
static void rawnand_check_cont_read_support(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
if (chip->read_retries)
return;
if (!nand_lp_exec_cont_read_page_op(chip, 0, 0, NULL,
mtd->writesize, true))
chip->controller->supported_op.cont_read = 1;
}
static void rawnand_late_check_supported_ops(struct nand_chip *chip)
{
/* The supported_op fields should not be set by individual drivers */
WARN_ON_ONCE(chip->controller->supported_op.cont_read);
if (!nand_has_exec_op(chip))
return;
rawnand_check_cont_read_support(chip);
}
/* /*
* Get the flash and manufacturer id and lookup if the type is supported. * Get the flash and manufacturer id and lookup if the type is supported.
*/ */
@ -5023,6 +5160,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
/* Select the device */ /* Select the device */
nand_select_target(chip, 0); nand_select_target(chip, 0);
rawnand_early_check_supported_ops(chip);
/* Send the command for reading device ID */ /* Send the command for reading device ID */
ret = nand_readid_op(chip, 0, id_data, 2); ret = nand_readid_op(chip, 0, id_data, 2);
if (ret) if (ret)
@ -6325,6 +6464,8 @@ static int nand_scan_tail(struct nand_chip *chip)
goto err_free_interface_config; goto err_free_interface_config;
} }
rawnand_late_check_supported_ops(chip);
/* /*
* Look for secure regions in the NAND chip. These regions are supposed * Look for secure regions in the NAND chip. These regions are supposed
* to be protected by a secure element like Trustzone. So the read/write * to be protected by a secure element like Trustzone. So the read/write

View File

@ -46,8 +46,7 @@ int nand_jedec_detect(struct nand_chip *chip)
if (!p) if (!p)
return -ENOMEM; return -ENOMEM;
if (!nand_has_exec_op(chip) || if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read)
!nand_read_data_op(chip, p, sizeof(*p), true, true))
use_datain = true; use_datain = true;
for (i = 0; i < JEDEC_PARAM_PAGES; i++) { for (i = 0; i < JEDEC_PARAM_PAGES; i++) {

View File

@ -166,8 +166,7 @@ int nand_onfi_detect(struct nand_chip *chip)
if (!pbuf) if (!pbuf)
return -ENOMEM; return -ENOMEM;
if (!nand_has_exec_op(chip) || if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read)
!nand_read_data_op(chip, &pbuf[0], sizeof(*pbuf), true, true))
use_datain = true; use_datain = true;
for (i = 0; i < ONFI_PARAM_PAGES; i++) { for (i = 0; i < ONFI_PARAM_PAGES; i++) {

View File

@ -26,9 +26,12 @@
#define CLE_PIN_CTL 15 #define CLE_PIN_CTL 15
#define ALE_PIN_CTL 14 #define ALE_PIN_CTL 14
static unsigned int lpcctl; struct pasemi_ddata {
static struct mtd_info *pasemi_nand_mtd; struct nand_chip chip;
static struct nand_controller controller; unsigned int lpcctl;
struct nand_controller controller;
};
static const char driver_name[] = "pasemi-nand"; static const char driver_name[] = "pasemi-nand";
static void pasemi_read_buf(struct nand_chip *chip, u_char *buf, int len) static void pasemi_read_buf(struct nand_chip *chip, u_char *buf, int len)
@ -55,6 +58,8 @@ static void pasemi_write_buf(struct nand_chip *chip, const u_char *buf,
static void pasemi_hwcontrol(struct nand_chip *chip, int cmd, static void pasemi_hwcontrol(struct nand_chip *chip, int cmd,
unsigned int ctrl) unsigned int ctrl)
{ {
struct pasemi_ddata *ddata = container_of(chip, struct pasemi_ddata, chip);
if (cmd == NAND_CMD_NONE) if (cmd == NAND_CMD_NONE)
return; return;
@ -65,12 +70,14 @@ static void pasemi_hwcontrol(struct nand_chip *chip, int cmd,
/* Push out posted writes */ /* Push out posted writes */
eieio(); eieio();
inl(lpcctl); inl(ddata->lpcctl);
} }
static int pasemi_device_ready(struct nand_chip *chip) static int pasemi_device_ready(struct nand_chip *chip)
{ {
return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); struct pasemi_ddata *ddata = container_of(chip, struct pasemi_ddata, chip);
return !!(inl(ddata->lpcctl) & LBICTRL_LPCCTL_NR);
} }
static int pasemi_attach_chip(struct nand_chip *chip) static int pasemi_attach_chip(struct nand_chip *chip)
@ -93,29 +100,31 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct resource res; struct resource res;
struct nand_chip *chip; struct nand_chip *chip;
struct nand_controller *controller;
int err = 0; int err = 0;
struct pasemi_ddata *ddata;
struct mtd_info *pasemi_nand_mtd;
err = of_address_to_resource(np, 0, &res); err = of_address_to_resource(np, 0, &res);
if (err) if (err)
return -EINVAL; return -EINVAL;
/* We only support one device at the moment */
if (pasemi_nand_mtd)
return -ENODEV;
dev_dbg(dev, "pasemi_nand at %pR\n", &res); dev_dbg(dev, "pasemi_nand at %pR\n", &res);
/* Allocate memory for MTD device structure and private data */ /* Allocate memory for MTD device structure and private data */
chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); ddata = kzalloc(sizeof(*ddata), GFP_KERNEL);
if (!chip) { if (!ddata) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
platform_set_drvdata(ofdev, ddata);
chip = &ddata->chip;
controller = &ddata->controller;
controller.ops = &pasemi_ops; controller->ops = &pasemi_ops;
nand_controller_init(&controller); nand_controller_init(controller);
chip->controller = &controller; chip->controller = controller;
pasemi_nand_mtd = nand_to_mtd(chip); pasemi_nand_mtd = nand_to_mtd(chip);
@ -136,10 +145,10 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
goto out_ior; goto out_ior;
} }
lpcctl = pci_resource_start(pdev, 0); ddata->lpcctl = pci_resource_start(pdev, 0);
pci_dev_put(pdev); pci_dev_put(pdev);
if (!request_region(lpcctl, 4, driver_name)) { if (!request_region(ddata->lpcctl, 4, driver_name)) {
err = -EBUSY; err = -EBUSY;
goto out_ior; goto out_ior;
} }
@ -172,45 +181,43 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
} }
dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res, dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res,
lpcctl); ddata->lpcctl);
return 0; return 0;
out_cleanup_nand: out_cleanup_nand:
nand_cleanup(chip); nand_cleanup(chip);
out_lpc: out_lpc:
release_region(lpcctl, 4); release_region(ddata->lpcctl, 4);
out_ior: out_ior:
iounmap(chip->legacy.IO_ADDR_R); iounmap(chip->legacy.IO_ADDR_R);
out_mtd: out_mtd:
kfree(chip); kfree(ddata);
out: out:
return err; return err;
} }
static int pasemi_nand_remove(struct platform_device *ofdev) static int pasemi_nand_remove(struct platform_device *ofdev)
{ {
struct nand_chip *chip; struct pasemi_ddata *ddata = platform_get_drvdata(ofdev);
struct mtd_info *pasemi_nand_mtd;
int ret; int ret;
struct nand_chip *chip;
if (!pasemi_nand_mtd) chip = &ddata->chip;
return 0; pasemi_nand_mtd = nand_to_mtd(chip);
chip = mtd_to_nand(pasemi_nand_mtd);
/* Release resources, unregister device */ /* Release resources, unregister device */
ret = mtd_device_unregister(pasemi_nand_mtd); ret = mtd_device_unregister(pasemi_nand_mtd);
WARN_ON(ret); WARN_ON(ret);
nand_cleanup(chip); nand_cleanup(chip);
release_region(lpcctl, 4); release_region(ddata->lpcctl, 4);
iounmap(chip->legacy.IO_ADDR_R); iounmap(chip->legacy.IO_ADDR_R);
/* Free the MTD device structure */ /* Free the MTD device structure */
kfree(chip); kfree(ddata);
pasemi_nand_mtd = NULL;
return 0; return 0;
} }

View File

@ -172,10 +172,10 @@ struct sunxi_nand_chip_sel {
/** /**
* struct sunxi_nand_hw_ecc - stores information related to HW ECC support * struct sunxi_nand_hw_ecc - stores information related to HW ECC support
* *
* @mode: the sunxi ECC mode field deduced from ECC requirements * @ecc_ctl: ECC_CTL register value for this NAND chip
*/ */
struct sunxi_nand_hw_ecc { struct sunxi_nand_hw_ecc {
int mode; u32 ecc_ctl;
}; };
/** /**
@ -193,7 +193,7 @@ struct sunxi_nand_hw_ecc {
struct sunxi_nand_chip { struct sunxi_nand_chip {
struct list_head node; struct list_head node;
struct nand_chip nand; struct nand_chip nand;
struct sunxi_nand_hw_ecc *ecc; struct sunxi_nand_hw_ecc ecc;
unsigned long clk_rate; unsigned long clk_rate;
u32 timing_cfg; u32 timing_cfg;
u32 timing_ctl; u32 timing_ctl;
@ -421,7 +421,7 @@ static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
struct sunxi_nand_chip_sel *sel; struct sunxi_nand_chip_sel *sel;
u32 ctl; u32 ctl;
if (cs > 0 && cs >= sunxi_nand->nsels) if (cs >= sunxi_nand->nsels)
return; return;
ctl = readl(nfc->regs + NFC_REG_CTL) & ctl = readl(nfc->regs + NFC_REG_CTL) &
@ -689,26 +689,15 @@ static void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand)
{ {
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
u32 ecc_ctl;
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); writel(sunxi_nand->ecc.ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
NFC_ECC_BLOCK_SIZE_MSK);
ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(sunxi_nand->ecc->mode) |
NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE;
if (nand->ecc.size == 512)
ecc_ctl |= NFC_ECC_BLOCK_512;
writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
} }
static void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand) static void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand)
{ {
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, writel(0, nfc->regs + NFC_REG_ECC_CTL);
nfc->regs + NFC_REG_ECC_CTL);
} }
static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
@ -1604,12 +1593,19 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
return 0; return 0;
} }
/*
* The controller does not provide access to OOB bytes
* past the end of the ECC data.
*/
if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
return -ERANGE;
oobregion->offset = section * (ecc->bytes + 4); oobregion->offset = section * (ecc->bytes + 4);
if (section < ecc->steps) if (section < ecc->steps)
oobregion->length = 4; oobregion->length = 4;
else else
oobregion->offset = mtd->oobsize - oobregion->offset; oobregion->length = mtd->oobsize - oobregion->offset;
return 0; return 0;
} }
@ -1619,11 +1615,6 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
.free = sunxi_nand_ooblayout_free, .free = sunxi_nand_ooblayout_free,
}; };
static void sunxi_nand_hw_ecc_ctrl_cleanup(struct sunxi_nand_chip *sunxi_nand)
{
kfree(sunxi_nand->ecc);
}
static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
struct nand_ecc_ctrl *ecc, struct nand_ecc_ctrl *ecc,
struct device_node *np) struct device_node *np)
@ -1634,7 +1625,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
struct mtd_info *mtd = nand_to_mtd(nand); struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_device *nanddev = mtd_to_nanddev(mtd); struct nand_device *nanddev = mtd_to_nanddev(mtd);
int nsectors; int nsectors;
int ret;
int i; int i;
if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
@ -1669,10 +1659,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
if (ecc->size != 512 && ecc->size != 1024) if (ecc->size != 512 && ecc->size != 1024)
return -EINVAL; return -EINVAL;
sunxi_nand->ecc = kzalloc(sizeof(*sunxi_nand->ecc), GFP_KERNEL);
if (!sunxi_nand->ecc)
return -ENOMEM;
/* Prefer 1k ECC chunk over 512 ones */ /* Prefer 1k ECC chunk over 512 ones */
if (ecc->size == 512 && mtd->writesize > 512) { if (ecc->size == 512 && mtd->writesize > 512) {
ecc->size = 1024; ecc->size = 1024;
@ -1693,12 +1679,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
if (i >= ARRAY_SIZE(strengths)) { if (i >= ARRAY_SIZE(strengths)) {
dev_err(nfc->dev, "unsupported strength\n"); dev_err(nfc->dev, "unsupported strength\n");
ret = -ENOTSUPP; return -ENOTSUPP;
goto err;
} }
sunxi_nand->ecc->mode = i;
/* HW ECC always request ECC bytes for 1024 bytes blocks */ /* HW ECC always request ECC bytes for 1024 bytes blocks */
ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
@ -1707,10 +1690,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
nsectors = mtd->writesize / ecc->size; nsectors = mtd->writesize / ecc->size;
if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { if (mtd->oobsize < ((ecc->bytes + 4) * nsectors))
ret = -EINVAL; return -EINVAL;
goto err;
}
ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
ecc->write_oob = sunxi_nfc_hw_ecc_write_oob; ecc->write_oob = sunxi_nfc_hw_ecc_write_oob;
@ -1732,26 +1713,13 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
ecc->read_oob_raw = nand_read_oob_std; ecc->read_oob_raw = nand_read_oob_std;
ecc->write_oob_raw = nand_write_oob_std; ecc->write_oob_raw = nand_write_oob_std;
sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION |
NFC_ECC_PIPELINE | NFC_ECC_EN;
if (ecc->size == 512)
sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512;
return 0; return 0;
err:
kfree(sunxi_nand->ecc);
return ret;
}
static void sunxi_nand_ecc_cleanup(struct sunxi_nand_chip *sunxi_nand)
{
struct nand_ecc_ctrl *ecc = &sunxi_nand->nand.ecc;
switch (ecc->engine_type) {
case NAND_ECC_ENGINE_TYPE_ON_HOST:
sunxi_nand_hw_ecc_ctrl_cleanup(sunxi_nand);
break;
case NAND_ECC_ENGINE_TYPE_NONE:
default:
break;
}
} }
static int sunxi_nand_attach_chip(struct nand_chip *nand) static int sunxi_nand_attach_chip(struct nand_chip *nand)
@ -1950,6 +1918,24 @@ static const struct nand_controller_ops sunxi_nand_controller_ops = {
.exec_op = sunxi_nfc_exec_op, .exec_op = sunxi_nfc_exec_op,
}; };
static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
{
struct sunxi_nand_chip *sunxi_nand;
struct nand_chip *chip;
int ret;
while (!list_empty(&nfc->chips)) {
sunxi_nand = list_first_entry(&nfc->chips,
struct sunxi_nand_chip,
node);
chip = &sunxi_nand->nand;
ret = mtd_device_unregister(nand_to_mtd(chip));
WARN_ON(ret);
nand_cleanup(chip);
list_del(&sunxi_nand->node);
}
}
static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
struct device_node *np) struct device_node *np)
{ {
@ -2041,18 +2027,13 @@ static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
{ {
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct device_node *nand_np; struct device_node *nand_np;
int nchips = of_get_child_count(np);
int ret; int ret;
if (nchips > 8) {
dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips);
return -EINVAL;
}
for_each_child_of_node(np, nand_np) { for_each_child_of_node(np, nand_np) {
ret = sunxi_nand_chip_init(dev, nfc, nand_np); ret = sunxi_nand_chip_init(dev, nfc, nand_np);
if (ret) { if (ret) {
of_node_put(nand_np); of_node_put(nand_np);
sunxi_nand_chips_cleanup(nfc);
return ret; return ret;
} }
} }
@ -2060,25 +2041,6 @@ static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
return 0; return 0;
} }
static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
{
struct sunxi_nand_chip *sunxi_nand;
struct nand_chip *chip;
int ret;
while (!list_empty(&nfc->chips)) {
sunxi_nand = list_first_entry(&nfc->chips,
struct sunxi_nand_chip,
node);
chip = &sunxi_nand->nand;
ret = mtd_device_unregister(nand_to_mtd(chip));
WARN_ON(ret);
nand_cleanup(chip);
sunxi_nand_ecc_cleanup(sunxi_nand);
list_del(&sunxi_nand->node);
}
}
static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r) static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r)
{ {
int ret; int ret;

View File

@ -1,533 +0,0 @@
/*
* Toshiba TMIO NAND flash controller driver
*
* Slightly murky pre-git history of the driver:
*
* Copyright (c) Ian Molton 2004, 2005, 2008
* Original work, independent of sharps code. Included hardware ECC support.
* Hard ECC did not work for writes in the early revisions.
* Copyright (c) Dirk Opfer 2005.
* Modifications developed from sharps code but
* NOT containing any, ported onto Ians base.
* Copyright (c) Chris Humbert 2005
* Copyright (c) Dmitry Baryshkov 2008
* Minor fixes
*
* Parts copyright Sebastian Carlier
*
* This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tmio.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
/*--------------------------------------------------------------------------*/
/*
* NAND Flash Host Controller Configuration Register
*/
#define CCR_COMMAND 0x04 /* w Command */
#define CCR_BASE 0x10 /* l NAND Flash Control Reg Base Addr */
#define CCR_INTP 0x3d /* b Interrupt Pin */
#define CCR_INTE 0x48 /* b Interrupt Enable */
#define CCR_EC 0x4a /* b Event Control */
#define CCR_ICC 0x4c /* b Internal Clock Control */
#define CCR_ECCC 0x5b /* b ECC Control */
#define CCR_NFTC 0x60 /* b NAND Flash Transaction Control */
#define CCR_NFM 0x61 /* b NAND Flash Monitor */
#define CCR_NFPSC 0x62 /* b NAND Flash Power Supply Control */
#define CCR_NFDC 0x63 /* b NAND Flash Detect Control */
/*
* NAND Flash Control Register
*/
#define FCR_DATA 0x00 /* bwl Data Register */
#define FCR_MODE 0x04 /* b Mode Register */
#define FCR_STATUS 0x05 /* b Status Register */
#define FCR_ISR 0x06 /* b Interrupt Status Register */
#define FCR_IMR 0x07 /* b Interrupt Mask Register */
/* FCR_MODE Register Command List */
#define FCR_MODE_DATA 0x94 /* Data Data_Mode */
#define FCR_MODE_COMMAND 0x95 /* Data Command_Mode */
#define FCR_MODE_ADDRESS 0x96 /* Data Address_Mode */
#define FCR_MODE_HWECC_CALC 0xB4 /* HW-ECC Data */
#define FCR_MODE_HWECC_RESULT 0xD4 /* HW-ECC Calc result Read_Mode */
#define FCR_MODE_HWECC_RESET 0xF4 /* HW-ECC Reset */
#define FCR_MODE_POWER_ON 0x0C /* Power Supply ON to SSFDC card */
#define FCR_MODE_POWER_OFF 0x08 /* Power Supply OFF to SSFDC card */
#define FCR_MODE_LED_OFF 0x00 /* LED OFF */
#define FCR_MODE_LED_ON 0x04 /* LED ON */
#define FCR_MODE_EJECT_ON 0x68 /* Ejection events active */
#define FCR_MODE_EJECT_OFF 0x08 /* Ejection events ignored */
#define FCR_MODE_LOCK 0x6C /* Lock_Mode. Eject Switch Invalid */
#define FCR_MODE_UNLOCK 0x0C /* UnLock_Mode. Eject Switch is valid */
#define FCR_MODE_CONTROLLER_ID 0x40 /* Controller ID Read */
#define FCR_MODE_STANDBY 0x00 /* SSFDC card Changes Standby State */
#define FCR_MODE_WE 0x80
#define FCR_MODE_ECC1 0x40
#define FCR_MODE_ECC0 0x20
#define FCR_MODE_CE 0x10
#define FCR_MODE_PCNT1 0x08
#define FCR_MODE_PCNT0 0x04
#define FCR_MODE_ALE 0x02
#define FCR_MODE_CLE 0x01
#define FCR_STATUS_BUSY 0x80
/*--------------------------------------------------------------------------*/
struct tmio_nand {
struct nand_controller controller;
struct nand_chip chip;
struct completion comp;
struct platform_device *dev;
void __iomem *ccr;
void __iomem *fcr;
unsigned long fcr_base;
unsigned int irq;
/* for tmio_nand_read_byte */
u8 read;
unsigned read_good:1;
};
static inline struct tmio_nand *mtd_to_tmio(struct mtd_info *mtd)
{
return container_of(mtd_to_nand(mtd), struct tmio_nand, chip);
}
/*--------------------------------------------------------------------------*/
static void tmio_nand_hwcontrol(struct nand_chip *chip, int cmd,
unsigned int ctrl)
{
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
if (ctrl & NAND_CTRL_CHANGE) {
u8 mode;
if (ctrl & NAND_NCE) {
mode = FCR_MODE_DATA;
if (ctrl & NAND_CLE)
mode |= FCR_MODE_CLE;
else
mode &= ~FCR_MODE_CLE;
if (ctrl & NAND_ALE)
mode |= FCR_MODE_ALE;
else
mode &= ~FCR_MODE_ALE;
} else {
mode = FCR_MODE_STANDBY;
}
tmio_iowrite8(mode, tmio->fcr + FCR_MODE);
tmio->read_good = 0;
}
if (cmd != NAND_CMD_NONE)
tmio_iowrite8(cmd, chip->legacy.IO_ADDR_W);
}
static int tmio_nand_dev_ready(struct nand_chip *chip)
{
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
return !(tmio_ioread8(tmio->fcr + FCR_STATUS) & FCR_STATUS_BUSY);
}
static irqreturn_t tmio_irq(int irq, void *__tmio)
{
struct tmio_nand *tmio = __tmio;
/* disable RDYREQ interrupt */
tmio_iowrite8(0x00, tmio->fcr + FCR_IMR);
complete(&tmio->comp);
return IRQ_HANDLED;
}
/*
*The TMIO core has a RDYREQ interrupt on the posedge of #SMRB.
*This interrupt is normally disabled, but for long operations like
*erase and write, we enable it to wake us up. The irq handler
*disables the interrupt.
*/
static int tmio_nand_wait(struct nand_chip *nand_chip)
{
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(nand_chip));
long timeout;
u8 status;
/* enable RDYREQ interrupt */
tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR);
reinit_completion(&tmio->comp);
tmio_iowrite8(0x81, tmio->fcr + FCR_IMR);
timeout = 400;
timeout = wait_for_completion_timeout(&tmio->comp,
msecs_to_jiffies(timeout));
if (unlikely(!tmio_nand_dev_ready(nand_chip))) {
tmio_iowrite8(0x00, tmio->fcr + FCR_IMR);
dev_warn(&tmio->dev->dev, "still busy after 400 ms\n");
} else if (unlikely(!timeout)) {
tmio_iowrite8(0x00, tmio->fcr + FCR_IMR);
dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n");
}
nand_status_op(nand_chip, &status);
return status;
}
/*
*The TMIO controller combines two 8-bit data bytes into one 16-bit
*word. This function separates them so nand_base.c works as expected,
*especially its NAND_CMD_READID routines.
*
*To prevent stale data from being read, tmio_nand_hwcontrol() clears
*tmio->read_good.
*/
static u_char tmio_nand_read_byte(struct nand_chip *chip)
{
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
unsigned int data;
if (tmio->read_good--)
return tmio->read;
data = tmio_ioread16(tmio->fcr + FCR_DATA);
tmio->read = data >> 8;
return data;
}
/*
*The TMIO controller converts an 8-bit NAND interface to a 16-bit
*bus interface, so all data reads and writes must be 16-bit wide.
*Thus, we implement 16-bit versions of the read, write, and verify
*buffer functions.
*/
static void
tmio_nand_write_buf(struct nand_chip *chip, const u_char *buf, int len)
{
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
tmio_iowrite16_rep(tmio->fcr + FCR_DATA, buf, len >> 1);
}
static void tmio_nand_read_buf(struct nand_chip *chip, u_char *buf, int len)
{
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1);
}
static void tmio_nand_enable_hwecc(struct nand_chip *chip, int mode)
{
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
tmio_iowrite8(FCR_MODE_HWECC_RESET, tmio->fcr + FCR_MODE);
tmio_ioread8(tmio->fcr + FCR_DATA); /* dummy read */
tmio_iowrite8(FCR_MODE_HWECC_CALC, tmio->fcr + FCR_MODE);
}
static int tmio_nand_calculate_ecc(struct nand_chip *chip, const u_char *dat,
u_char *ecc_code)
{
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
unsigned int ecc;
tmio_iowrite8(FCR_MODE_HWECC_RESULT, tmio->fcr + FCR_MODE);
ecc = tmio_ioread16(tmio->fcr + FCR_DATA);
ecc_code[1] = ecc; /* 000-255 LP7-0 */
ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */
ecc = tmio_ioread16(tmio->fcr + FCR_DATA);
ecc_code[2] = ecc; /* 000-255 CP5-0,11b */
ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */
ecc = tmio_ioread16(tmio->fcr + FCR_DATA);
ecc_code[3] = ecc; /* 256-511 LP15-8 */
ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */
tmio_iowrite8(FCR_MODE_DATA, tmio->fcr + FCR_MODE);
return 0;
}
static int tmio_nand_correct_data(struct nand_chip *chip, unsigned char *buf,
unsigned char *read_ecc,
unsigned char *calc_ecc)
{
int r0, r1;
/* assume ecc.size = 512 and ecc.bytes = 6 */
r0 = rawnand_sw_hamming_correct(chip, buf, read_ecc, calc_ecc);
if (r0 < 0)
return r0;
r1 = rawnand_sw_hamming_correct(chip, buf + 256, read_ecc + 3,
calc_ecc + 3);
if (r1 < 0)
return r1;
return r0 + r1;
}
static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
int ret;
if (cell->enable) {
ret = cell->enable(dev);
if (ret)
return ret;
}
/* (4Ch) CLKRUN Enable 1st spcrunc */
tmio_iowrite8(0x81, tmio->ccr + CCR_ICC);
/* (10h)BaseAddress 0x1000 spba.spba2 */
tmio_iowrite16(tmio->fcr_base, tmio->ccr + CCR_BASE);
tmio_iowrite16(tmio->fcr_base >> 16, tmio->ccr + CCR_BASE + 2);
/* (04h)Command Register I/O spcmd */
tmio_iowrite8(0x02, tmio->ccr + CCR_COMMAND);
/* (62h) Power Supply Control ssmpwc */
/* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */
tmio_iowrite8(0x02, tmio->ccr + CCR_NFPSC);
/* (63h) Detect Control ssmdtc */
tmio_iowrite8(0x02, tmio->ccr + CCR_NFDC);
/* Interrupt status register clear sintst */
tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR);
/* After power supply, Media are reset smode */
tmio_iowrite8(FCR_MODE_POWER_ON, tmio->fcr + FCR_MODE);
tmio_iowrite8(FCR_MODE_COMMAND, tmio->fcr + FCR_MODE);
tmio_iowrite8(NAND_CMD_RESET, tmio->fcr + FCR_DATA);
/* Standby Mode smode */
tmio_iowrite8(FCR_MODE_STANDBY, tmio->fcr + FCR_MODE);
mdelay(5);
return 0;
}
static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE);
if (cell->disable)
cell->disable(dev);
}
static int tmio_attach_chip(struct nand_chip *chip)
{
if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
return 0;
chip->ecc.size = 512;
chip->ecc.bytes = 6;
chip->ecc.strength = 2;
chip->ecc.hwctl = tmio_nand_enable_hwecc;
chip->ecc.calculate = tmio_nand_calculate_ecc;
chip->ecc.correct = tmio_nand_correct_data;
return 0;
}
static const struct nand_controller_ops tmio_ops = {
.attach_chip = tmio_attach_chip,
};
static int tmio_probe(struct platform_device *dev)
{
struct tmio_nand_data *data = dev_get_platdata(&dev->dev);
struct resource *fcr = platform_get_resource(dev,
IORESOURCE_MEM, 0);
struct resource *ccr = platform_get_resource(dev,
IORESOURCE_MEM, 1);
int irq = platform_get_irq(dev, 0);
struct tmio_nand *tmio;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
int retval;
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;
init_completion(&tmio->comp);
tmio->dev = dev;
platform_set_drvdata(dev, tmio);
nand_chip = &tmio->chip;
mtd = nand_to_mtd(nand_chip);
mtd->name = "tmio-nand";
mtd->dev.parent = &dev->dev;
nand_controller_init(&tmio->controller);
tmio->controller.ops = &tmio_ops;
nand_chip->controller = &tmio->controller;
tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr));
if (!tmio->ccr)
return -EIO;
tmio->fcr_base = fcr->start & 0xfffff;
tmio->fcr = devm_ioremap(&dev->dev, fcr->start, resource_size(fcr));
if (!tmio->fcr)
return -EIO;
retval = tmio_hw_init(dev, tmio);
if (retval)
return retval;
/* Set address of NAND IO lines */
nand_chip->legacy.IO_ADDR_R = tmio->fcr;
nand_chip->legacy.IO_ADDR_W = tmio->fcr;
/* Set address of hardware control function */
nand_chip->legacy.cmd_ctrl = tmio_nand_hwcontrol;
nand_chip->legacy.dev_ready = tmio_nand_dev_ready;
nand_chip->legacy.read_byte = tmio_nand_read_byte;
nand_chip->legacy.write_buf = tmio_nand_write_buf;
nand_chip->legacy.read_buf = tmio_nand_read_buf;
if (data)
nand_chip->badblock_pattern = data->badblock_pattern;
/* 15 us command delay time */
nand_chip->legacy.chip_delay = 15;
retval = devm_request_irq(&dev->dev, irq, &tmio_irq, 0,
dev_name(&dev->dev), tmio);
if (retval) {
dev_err(&dev->dev, "request_irq error %d\n", retval);
goto err_irq;
}
tmio->irq = irq;
nand_chip->legacy.waitfunc = tmio_nand_wait;
/* Scan to find existence of the device */
retval = nand_scan(nand_chip, 1);
if (retval)
goto err_irq;
/* Register the partitions */
retval = mtd_device_parse_register(mtd,
data ? data->part_parsers : NULL,
NULL,
data ? data->partition : NULL,
data ? data->num_partitions : 0);
if (!retval)
return retval;
nand_cleanup(nand_chip);
err_irq:
tmio_hw_stop(dev, tmio);
return retval;
}
static int tmio_remove(struct platform_device *dev)
{
struct tmio_nand *tmio = platform_get_drvdata(dev);
struct nand_chip *chip = &tmio->chip;
int ret;
ret = mtd_device_unregister(nand_to_mtd(chip));
WARN_ON(ret);
nand_cleanup(chip);
tmio_hw_stop(dev, tmio);
return 0;
}
#ifdef CONFIG_PM
static int tmio_suspend(struct platform_device *dev, pm_message_t state)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
if (cell->suspend)
cell->suspend(dev);
tmio_hw_stop(dev, platform_get_drvdata(dev));
return 0;
}
static int tmio_resume(struct platform_device *dev)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
/* FIXME - is this required or merely another attack of the broken
* SHARP platform? Looks suspicious.
*/
tmio_hw_init(dev, platform_get_drvdata(dev));
if (cell->resume)
cell->resume(dev);
return 0;
}
#else
#define tmio_suspend NULL
#define tmio_resume NULL
#endif
static struct platform_driver tmio_driver = {
.driver.name = "tmio-nand",
.driver.owner = THIS_MODULE,
.probe = tmio_probe,
.remove = tmio_remove,
.suspend = tmio_suspend,
.resume = tmio_resume,
};
module_platform_driver(tmio_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Ian Molton, Dirk Opfer, Chris Humbert, Dmitry Baryshkov");
MODULE_DESCRIPTION("NAND flash driver on Toshiba Mobile IO controller");
MODULE_ALIAS("platform:tmio-nand");

View File

@ -206,7 +206,7 @@ static inline bool vf610_nfc_kernel_is_little_endian(void)
#endif #endif
} }
/** /*
* Read accessor for internal SRAM buffer * Read accessor for internal SRAM buffer
* @dst: destination address in regular memory * @dst: destination address in regular memory
* @src: source address in SRAM buffer * @src: source address in SRAM buffer
@ -241,7 +241,7 @@ static inline void vf610_nfc_rd_from_sram(void *dst, const void __iomem *src,
} }
} }
/** /*
* Write accessor for internal SRAM buffer * Write accessor for internal SRAM buffer
* @dst: destination address in SRAM buffer * @dst: destination address in SRAM buffer
* @src: source address in regular memory * @src: source address in regular memory

View File

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

View File

@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Author: Mario Kicherer <dev@kicherer.org>
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
#define SPINAND_MFR_ALLIANCEMEMORY 0x52
#define AM_STATUS_ECC_BITMASK (3 << 4)
#define AM_STATUS_ECC_NONE_DETECTED (0 << 4)
#define AM_STATUS_ECC_CORRECTED (1 << 4)
#define AM_STATUS_ECC_ERRORED (2 << 4)
#define AM_STATUS_ECC_MAX_CORRECTED (3 << 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 am_get_eccsize(struct mtd_info *mtd)
{
if (mtd->oobsize == 64)
return 0x20;
else if (mtd->oobsize == 128)
return 0x38;
else if (mtd->oobsize == 256)
return 0x70;
else
return -EINVAL;
}
static int am_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
int ecc_bytes;
ecc_bytes = am_get_eccsize(mtd);
if (ecc_bytes < 0)
return ecc_bytes;
region->offset = mtd->oobsize - ecc_bytes;
region->length = ecc_bytes;
return 0;
}
static int am_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
int ecc_bytes;
if (section)
return -ERANGE;
ecc_bytes = am_get_eccsize(mtd);
if (ecc_bytes < 0)
return ecc_bytes;
/*
* It is unclear how many bytes are used for the bad block marker. We
* reserve the common two bytes here.
*
* The free area in this kind of flash is divided into chunks where the
* first 4 bytes of each chunk are unprotected. The number of chunks
* depends on the specific model. The models with 4096+256 bytes pages
* have 8 chunks, the others 4 chunks.
*/
region->offset = 2;
region->length = mtd->oobsize - 2 - ecc_bytes;
return 0;
}
static const struct mtd_ooblayout_ops am_ooblayout = {
.ecc = am_ooblayout_ecc,
.free = am_ooblayout_free,
};
static int am_ecc_get_status(struct spinand_device *spinand, u8 status)
{
switch (status & AM_STATUS_ECC_BITMASK) {
case AM_STATUS_ECC_NONE_DETECTED:
return 0;
case AM_STATUS_ECC_CORRECTED:
/*
* use oobsize to determine the flash model and the maximum of
* correctable errors and return maximum - 1 by convention
*/
if (spinand->base.mtd.oobsize == 64)
return 3;
else
return 7;
case AM_STATUS_ECC_ERRORED:
return -EBADMSG;
case AM_STATUS_ECC_MAX_CORRECTED:
/*
* use oobsize to determine the flash model and the maximum of
* correctable errors
*/
if (spinand->base.mtd.oobsize == 64)
return 4;
else
return 8;
default:
break;
}
return -EINVAL;
}
static const struct spinand_info alliancememory_spinand_table[] = {
SPINAND_INFO("AS5F34G04SND",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x2f),
NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&am_ooblayout,
am_ecc_get_status)),
};
static const struct spinand_manufacturer_ops alliancememory_spinand_manuf_ops = {
};
const struct spinand_manufacturer alliancememory_spinand_manufacturer = {
.id = SPINAND_MFR_ALLIANCEMEMORY,
.name = "AllianceMemory",
.chips = alliancememory_spinand_table,
.nchips = ARRAY_SIZE(alliancememory_spinand_table),
.ops = &alliancememory_spinand_manuf_ops,
};

View File

@ -937,6 +937,7 @@ static const struct nand_ops spinand_ops = {
}; };
static const struct spinand_manufacturer *spinand_manufacturers[] = { static const struct spinand_manufacturer *spinand_manufacturers[] = {
&alliancememory_spinand_manufacturer,
&ato_spinand_manufacturer, &ato_spinand_manufacturer,
&gigadevice_spinand_manufacturer, &gigadevice_spinand_manufacturer,
&macronix_spinand_manufacturer, &macronix_spinand_manufacturer,

View File

@ -83,9 +83,10 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
* in order to avoid forcing the wear-leveling layer to move * in order to avoid forcing the wear-leveling layer to move
* data around if it's not necessary. * data around if it's not necessary.
*/ */
if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr)) if (mx35lf1ge4ab_get_eccsr(spinand, spinand->scratchbuf))
return nanddev_get_ecc_conf(nand)->strength; return nanddev_get_ecc_conf(nand)->strength;
eccsr = *spinand->scratchbuf;
if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength || if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength ||
!eccsr)) !eccsr))
return nanddev_get_ecc_conf(nand)->strength; return nanddev_get_ecc_conf(nand)->strength;

View File

@ -67,6 +67,8 @@ struct gpio_desc;
/* Extended commands for large page devices */ /* Extended commands for large page devices */
#define NAND_CMD_READSTART 0x30 #define NAND_CMD_READSTART 0x30
#define NAND_CMD_READCACHESEQ 0x31
#define NAND_CMD_READCACHEEND 0x3f
#define NAND_CMD_RNDOUTSTART 0xE0 #define NAND_CMD_RNDOUTSTART 0xE0
#define NAND_CMD_CACHEDPROG 0x15 #define NAND_CMD_CACHEDPROG 0x15
@ -1094,10 +1096,20 @@ struct nand_controller_ops {
* *
* @lock: lock used to serialize accesses to the NAND controller * @lock: lock used to serialize accesses to the NAND controller
* @ops: NAND controller operations. * @ops: NAND controller operations.
* @supported_op: NAND controller known-to-be-supported operations,
* only writable by the core after initial checking.
* @supported_op.data_only_read: The controller supports reading more data from
* the bus without restarting an entire read operation nor
* changing the column.
* @supported_op.cont_read: The controller supports sequential cache reads.
*/ */
struct nand_controller { struct nand_controller {
struct mutex lock; struct mutex lock;
const struct nand_controller_ops *ops; const struct nand_controller_ops *ops;
struct {
unsigned int data_only_read: 1;
unsigned int cont_read: 1;
} supported_op;
}; };
static inline void nand_controller_init(struct nand_controller *nfc) static inline void nand_controller_init(struct nand_controller *nfc)
@ -1248,6 +1260,10 @@ struct nand_secure_region {
* @read_retries: The number of read retry modes supported * @read_retries: The number of read retry modes supported
* @secure_regions: Structure containing the secure regions info * @secure_regions: Structure containing the secure regions info
* @nr_secure_regions: Number of secure regions * @nr_secure_regions: Number of secure regions
* @cont_read: Sequential page read internals
* @cont_read.ongoing: Whether a continuous read is ongoing or not
* @cont_read.first_page: Start of the continuous read operation
* @cont_read.last_page: End of the continuous read operation
* @controller: The hardware controller structure which is shared among multiple * @controller: The hardware controller structure which is shared among multiple
* independent devices * independent devices
* @ecc: The ECC controller structure * @ecc: The ECC controller structure
@ -1300,6 +1316,11 @@ struct nand_chip {
int read_retries; int read_retries;
struct nand_secure_region *secure_regions; struct nand_secure_region *secure_regions;
u8 nr_secure_regions; u8 nr_secure_regions;
struct {
bool ongoing;
unsigned int first_page;
unsigned int last_page;
} cont_read;
/* Externals */ /* Externals */
struct nand_controller *controller; struct nand_controller *controller;

View File

@ -260,6 +260,7 @@ struct spinand_manufacturer {
}; };
/* SPI NAND manufacturers */ /* SPI NAND manufacturers */
extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
extern const struct spinand_manufacturer ato_spinand_manufacturer; extern const struct spinand_manufacturer ato_spinand_manufacturer;
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer;