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:
commit
f4440abc08
|
@ -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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -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";
|
||||||
|
};
|
||||||
|
};
|
|
@ -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";
|
|
||||||
};
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
|
@ -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,
|
||||||
¯onix_spinand_manufacturer,
|
¯onix_spinand_manufacturer,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue