diff options
author | Miquel Raynal <miquel.raynal@bootlin.com> | 2023-02-23 10:28:29 +0100 |
---|---|---|
committer | Miquel Raynal <miquel.raynal@bootlin.com> | 2023-02-23 10:28:29 +0100 |
commit | f4440abc08917d9a7032abb8a6a5d4b36ca979b6 (patch) | |
tree | 7a17631729c10d6778e2be007dd4f3f59a5f0c3c | |
parent | 27121864ab366992583d894961ecdc6e2ffe0ca1 (diff) | |
parent | ef3e6327ff04af8527b3558e023e99f1cc241bce (diff) |
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
22 files changed, 663 insertions, 840 deletions
diff --git a/Documentation/devicetree/bindings/mtd/mediatek,mtk-nfc.yaml b/Documentation/devicetree/bindings/mtd/mediatek,mtk-nfc.yaml new file mode 100644 index 000000000000..a6e7f123eda7 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/mediatek,mtk-nfc.yaml @@ -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>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml b/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml new file mode 100644 index 000000000000..505baf1e8830 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml @@ -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"; + }; + }; diff --git a/Documentation/devicetree/bindings/mtd/mtk-nand.txt b/Documentation/devicetree/bindings/mtd/mtk-nand.txt deleted file mode 100644 index 839ea2f93d04..000000000000 --- a/Documentation/devicetree/bindings/mtd/mtk-nand.txt +++ /dev/null @@ -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"; - }; diff --git a/MAINTAINERS b/MAINTAINERS index 36c500e34508..d0d29cc379d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13213,7 +13213,7 @@ F: drivers/phy/ralink/phy-mt7621-pci.c MEDIATEK NAND CONTROLLER DRIVER L: linux-mtd@lists.infradead.org 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_* MEDIATEK PMIC LED DRIVER diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c index 9f9b201fe706..c75bb8b80cc1 100644 --- a/drivers/mtd/nand/ecc-mtk.c +++ b/drivers/mtd/nand/ecc-mtk.c @@ -40,6 +40,10 @@ #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_ERRMASK_MT7622 GENMASK(4, 0) +#define ECC_ERRMASK_MT2701 GENMASK(5, 0) +#define ECC_ERRMASK_MT2712 GENMASK(6, 0) + struct mtk_ecc_caps { u32 err_mask; u32 err_shift; @@ -79,6 +83,10 @@ static const u8 ecc_strength_mt7622[] = { 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 { ECC_ENCPAR00, 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); static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = { - .err_mask = 0x3f, + .err_mask = ECC_ERRMASK_MT2701, .err_shift = 8, .ecc_strength = ecc_strength_mt2701, .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 = { - .err_mask = 0x7f, + .err_mask = ECC_ERRMASK_MT2712, .err_shift = 8, .ecc_strength = ecc_strength_mt2712, .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 = { - .err_mask = 0x1f, + .err_mask = ECC_ERRMASK_MT7622, .err_shift = 5, .ecc_strength = ecc_strength_mt7622, .ecc_regs = mt7622_ecc_regs, @@ -483,6 +491,17 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = { .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[] = { { .compatible = "mediatek,mt2701-ecc", @@ -493,6 +512,9 @@ static const struct of_device_id mtk_ecc_dt_match[] = { }, { .compatible = "mediatek,mt7622-ecc", .data = &mtk_ecc_caps_mt7622, + }, { + .compatible = "mediatek,mt7986-ecc", + .data = &mtk_ecc_caps_mt7986, }, {}, }; diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 98ea1c9e65c8..75a445200434 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -193,13 +193,6 @@ config MTD_NAND_PASEMI Enables support for NAND Flash interface on PA Semi PWRficient 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" config MTD_NAND_BCM47XXNFLASH diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index fa1d00120310..917cdfb815b9 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -23,7 +23,6 @@ omap2_nand-objs := omap2.o obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.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_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index a18d121396aa..e25119e58b69 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -725,6 +725,7 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip) struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; unsigned int al; + u32 br; /* * 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; } + /* 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 */ al = 0; if (chip->pagemask & 0xffff0000) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 42c64dcea767..3034916d2e25 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -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, 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,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, 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, 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), }; /** diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c3cc66039925..a6af521832aa 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -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); } +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 * @chip: The NAND chip @@ -1233,10 +1300,16 @@ int nand_read_page_op(struct nand_chip *chip, unsigned int page, return -EINVAL; if (nand_has_exec_op(chip)) { - if (mtd->writesize > 512) - return nand_lp_exec_read_page_op(chip, page, - offset_in_page, buf, - len); + if (mtd->writesize > 512) { + if (rawnand_cont_read_ongoing(chip, page)) + return nand_lp_exec_cont_read_page_op(chip, page, + 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, buf, len); @@ -3353,6 +3426,27 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, 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 * @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_required = oob ? 1 : 0; + rawnand_enable_cont_reads(chip, page, readlen, col); + while (1) { 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"; } +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. */ @@ -5023,6 +5160,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) /* Select the device */ nand_select_target(chip, 0); + rawnand_early_check_supported_ops(chip); + /* Send the command for reading device ID */ ret = nand_readid_op(chip, 0, id_data, 2); if (ret) @@ -6325,6 +6464,8 @@ static int nand_scan_tail(struct nand_chip *chip) goto err_free_interface_config; } + rawnand_late_check_supported_ops(chip); + /* * 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 diff --git a/drivers/mtd/nand/raw/nand_jedec.c b/drivers/mtd/nand/raw/nand_jedec.c index 85b6d9372d80..836757717660 100644 --- a/drivers/mtd/nand/raw/nand_jedec.c +++ b/drivers/mtd/nand/raw/nand_jedec.c @@ -46,8 +46,7 @@ int nand_jedec_detect(struct nand_chip *chip) if (!p) return -ENOMEM; - if (!nand_has_exec_op(chip) || - !nand_read_data_op(chip, p, sizeof(*p), true, true)) + if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read) use_datain = true; for (i = 0; i < JEDEC_PARAM_PAGES; i++) { diff --git a/drivers/mtd/nand/raw/nand_onfi.c b/drivers/mtd/nand/raw/nand_onfi.c index 7586befce7f9..f15ef90aec8c 100644 --- a/drivers/mtd/nand/raw/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -166,8 +166,7 @@ int nand_onfi_detect(struct nand_chip *chip) if (!pbuf) return -ENOMEM; - if (!nand_has_exec_op(chip) || - !nand_read_data_op(chip, &pbuf[0], sizeof(*pbuf), true, true)) + if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read) use_datain = true; for (i = 0; i < ONFI_PARAM_PAGES; i++) { diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c index c176036453ed..f7ef6ca06ca9 100644 --- a/drivers/mtd/nand/raw/pasemi_nand.c +++ b/drivers/mtd/nand/raw/pasemi_nand.c @@ -26,9 +26,12 @@ #define CLE_PIN_CTL 15 #define ALE_PIN_CTL 14 -static unsigned int lpcctl; -static struct mtd_info *pasemi_nand_mtd; -static struct nand_controller controller; +struct pasemi_ddata { + struct nand_chip chip; + unsigned int lpcctl; + struct nand_controller controller; +}; + static const char driver_name[] = "pasemi-nand"; 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, unsigned int ctrl) { + struct pasemi_ddata *ddata = container_of(chip, struct pasemi_ddata, chip); + if (cmd == NAND_CMD_NONE) return; @@ -65,12 +70,14 @@ static void pasemi_hwcontrol(struct nand_chip *chip, int cmd, /* Push out posted writes */ eieio(); - inl(lpcctl); + inl(ddata->lpcctl); } 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) @@ -93,29 +100,31 @@ static int pasemi_nand_probe(struct platform_device *ofdev) struct device_node *np = dev->of_node; struct resource res; struct nand_chip *chip; + struct nand_controller *controller; int err = 0; + struct pasemi_ddata *ddata; + struct mtd_info *pasemi_nand_mtd; err = of_address_to_resource(np, 0, &res); if (err) 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); /* Allocate memory for MTD device structure and private data */ - chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); - if (!chip) { + ddata = kzalloc(sizeof(*ddata), GFP_KERNEL); + if (!ddata) { err = -ENOMEM; goto out; } + platform_set_drvdata(ofdev, ddata); + chip = &ddata->chip; + controller = &ddata->controller; - controller.ops = &pasemi_ops; - nand_controller_init(&controller); - chip->controller = &controller; + controller->ops = &pasemi_ops; + nand_controller_init(controller); + chip->controller = controller; pasemi_nand_mtd = nand_to_mtd(chip); @@ -136,10 +145,10 @@ static int pasemi_nand_probe(struct platform_device *ofdev) goto out_ior; } - lpcctl = pci_resource_start(pdev, 0); + ddata->lpcctl = pci_resource_start(pdev, 0); pci_dev_put(pdev); - if (!request_region(lpcctl, 4, driver_name)) { + if (!request_region(ddata->lpcctl, 4, driver_name)) { err = -EBUSY; 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, - lpcctl); + ddata->lpcctl); return 0; out_cleanup_nand: nand_cleanup(chip); out_lpc: - release_region(lpcctl, 4); + release_region(ddata->lpcctl, 4); out_ior: iounmap(chip->legacy.IO_ADDR_R); out_mtd: - kfree(chip); + kfree(ddata); out: return err; } 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; + struct nand_chip *chip; - if (!pasemi_nand_mtd) - return 0; - - chip = mtd_to_nand(pasemi_nand_mtd); + chip = &ddata->chip; + pasemi_nand_mtd = nand_to_mtd(chip); /* Release resources, unregister device */ ret = mtd_device_unregister(pasemi_nand_mtd); WARN_ON(ret); nand_cleanup(chip); - release_region(lpcctl, 4); + release_region(ddata->lpcctl, 4); iounmap(chip->legacy.IO_ADDR_R); /* Free the MTD device structure */ - kfree(chip); - - pasemi_nand_mtd = NULL; + kfree(ddata); return 0; } diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index ea953e31933e..13e3e0198d15 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -172,10 +172,10 @@ struct sunxi_nand_chip_sel { /** * 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 { - int mode; + u32 ecc_ctl; }; /** @@ -193,7 +193,7 @@ struct sunxi_nand_hw_ecc { struct sunxi_nand_chip { struct list_head node; struct nand_chip nand; - struct sunxi_nand_hw_ecc *ecc; + struct sunxi_nand_hw_ecc ecc; unsigned long clk_rate; u32 timing_cfg; 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; u32 ctl; - if (cs > 0 && cs >= sunxi_nand->nsels) + if (cs >= sunxi_nand->nsels) return; 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_nfc *nfc = to_sunxi_nfc(nand->controller); - u32 ecc_ctl; - - ecc_ctl = readl(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); + writel(sunxi_nand->ecc.ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); } static void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, - nfc->regs + NFC_REG_ECC_CTL); + writel(0, nfc->regs + NFC_REG_ECC_CTL); } 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; } + /* + * 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); if (section < ecc->steps) oobregion->length = 4; else - oobregion->offset = mtd->oobsize - oobregion->offset; + oobregion->length = mtd->oobsize - oobregion->offset; return 0; } @@ -1619,11 +1615,6 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { .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, struct nand_ecc_ctrl *ecc, 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 nand_device *nanddev = mtd_to_nanddev(mtd); int nsectors; - int ret; int i; 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) 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 */ if (ecc->size == 512 && mtd->writesize > 512) { ecc->size = 1024; @@ -1693,12 +1679,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, if (i >= ARRAY_SIZE(strengths)) { dev_err(nfc->dev, "unsupported strength\n"); - ret = -ENOTSUPP; - goto err; + return -ENOTSUPP; } - sunxi_nand->ecc->mode = i; - /* HW ECC always request ECC bytes for 1024 bytes blocks */ 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; - if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { - ret = -EINVAL; - goto err; - } + if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) + return -EINVAL; ecc->read_oob = sunxi_nfc_hw_ecc_read_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->write_oob_raw = nand_write_oob_std; - return 0; - -err: - kfree(sunxi_nand->ecc); + sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION | + NFC_ECC_PIPELINE | NFC_ECC_EN; - return ret; -} + if (ecc->size == 512) + sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512; -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; - } + return 0; } 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, }; +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, 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 *nand_np; - int nchips = of_get_child_count(np); 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) { ret = sunxi_nand_chip_init(dev, nfc, nand_np); if (ret) { of_node_put(nand_np); + sunxi_nand_chips_cleanup(nfc); return ret; } } @@ -2060,25 +2041,6 @@ static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) 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) { int ret; diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c deleted file mode 100644 index 8f1a42bf199c..000000000000 --- a/drivers/mtd/nand/raw/tmio_nand.c +++ /dev/null @@ -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"); diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index a2b89b75073f..b643332ea1ff 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -206,7 +206,7 @@ static inline bool vf610_nfc_kernel_is_little_endian(void) #endif } -/** +/* * Read accessor for internal SRAM buffer * @dst: destination address in regular memory * @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 * @dst: destination address in SRAM buffer * @src: source address in regular memory diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index b520fe634041..4ec973b8b6bf 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # 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 diff --git a/drivers/mtd/nand/spi/alliancememory.c b/drivers/mtd/nand/spi/alliancememory.c new file mode 100644 index 000000000000..7936ea546b03 --- /dev/null +++ b/drivers/mtd/nand/spi/alliancememory.c @@ -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, +}; diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index dacd9c0e8b20..638391f77d8c 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -937,6 +937,7 @@ static const struct nand_ops spinand_ops = { }; static const struct spinand_manufacturer *spinand_manufacturers[] = { + &alliancememory_spinand_manufacturer, &ato_spinand_manufacturer, &gigadevice_spinand_manufacturer, ¯onix_spinand_manufacturer, diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index dce835132a1e..722a9738ba37 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -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 * 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; + eccsr = *spinand->scratchbuf; if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength || !eccsr)) return nanddev_get_ecc_conf(nand)->strength; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index dcf90144d70b..f8d4be9c587a 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -67,6 +67,8 @@ struct gpio_desc; /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 +#define NAND_CMD_READCACHESEQ 0x31 +#define NAND_CMD_READCACHEEND 0x3f #define NAND_CMD_RNDOUTSTART 0xE0 #define NAND_CMD_CACHEDPROG 0x15 @@ -1094,10 +1096,20 @@ struct nand_controller_ops { * * @lock: lock used to serialize accesses to the NAND controller * @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 mutex lock; 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) @@ -1248,6 +1260,10 @@ struct nand_secure_region { * @read_retries: The number of read retry modes supported * @secure_regions: Structure containing the secure regions info * @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 * independent devices * @ecc: The ECC controller structure @@ -1300,6 +1316,11 @@ struct nand_chip { int read_retries; struct nand_secure_region *secure_regions; u8 nr_secure_regions; + struct { + bool ongoing; + unsigned int first_page; + unsigned int last_page; + } cont_read; /* Externals */ struct nand_controller *controller; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 6d3392a7edc6..01be9f0f008a 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -260,6 +260,7 @@ struct spinand_manufacturer { }; /* SPI NAND manufacturers */ +extern const struct spinand_manufacturer alliancememory_spinand_manufacturer; extern const struct spinand_manufacturer ato_spinand_manufacturer; extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer; |