mirror of https://gitee.com/openkylin/linux.git
MTD core:
* Fix refcounting for unpartitioned MTDs * Fix misspelled function parameter 'section' * Remove unneeded break * cmdline parser: Fix parsing of part-names with colons * mtdpart: Fix misdocumented function parameter 'mtd' MTD devices: * phram: - Allow the user to set the erase page size - File headers are not good candidates for kernel-doc * physmap-bt1-rom: Fix __iomem addrspace removal warning * plat-ram: correctly free memory on error path in platram_probe() * powernv_flash: Add function names to headers and fix 'dev' * docg3: Fix kernel-doc 'bad line' and 'excessive doc' issues UBI cleanup fixes: * gluebi: Fix misnamed function parameter documentation * wl: Fix a couple of kernel-doc issues * eba: Fix a couple of misdocumentation issues * kapi: Correct documentation for 'ubi_leb_read_sg's 'sgl' parameter * Document 'ubi_num' in struct mtd_dev_param Generic NAND core: * ECC management: - Add an I/O request tweaking mechanism - Entire rework of the software BCH ECC driver, creation of a real ECC engine, getting rid of raw NAND structures, migration to more generic prototypes, misc fixes and style cleanup. Moved now to the Generic NAND layer. - Entire rework of the software Hamming ECC driver, creation of a real ECC engine, getting rid of raw NAND structures, misc renames, comment updates, cleanup, and style fixes. Moved now to the generic NAND layer. - Necessary plumbing at the NAND level to retrieve generic NAND ECC engines (softwares and on-die). - Update of the bindings. Raw NAND core: * Geting rid of the chip->ecc.priv entry. * Fix miscellaneous typos in kernel-doc Raw NAND controller drivers: * Arasan: Document 'anfc_op's 'buf' member * AU1550: Ensure the presence of the right includes * Brcmnand: Demote non-conformant kernel-doc headers * Cafe: Remove superfluous param doc and add another * Davinci: Do not use extra dereferencing * Diskonchip: Marking unused variables as __always_unused * GPMI: - Fix the driver only sense CS0 R/B issue - Fix the random DMA timeout issue - Use a single line for of_device_id - Use of_device_get_match_data() - Fix reference count leak in gpmi ops - Cleanup makefile - Fix binding matching of clocks on different SoCs * Ingenic: remove redundant get_device() in ingenic_ecc_get() * Intel LGM: New NAND controller driver * Marvell: Drop useless line * Meson: - Fix a resource leak in init - Fix meson_nfc_dma_buffer_release() arguments * mxc: - Use device_get_match_data() - Use a single line for of_device_id - Remove platform data support * Omap: - Fix a bunch of kernel-doc misdemeanours - Finish ELM half populated function header, demote empty ones * s3c2410: Add documentation for 2 missing struct members * Sunxi: Document 'sunxi_nfc's 'caps' member * Qcom: - Add support for SDX55 - Support for IPQ6018 QPIC NAND controller - Fix DMA sync on FLASH_STATUS register read * Rockchip: New NAND controller driver for RK3308, RK2928 and others * Sunxi: Add MDMA support ONENAND: * bbt: Fix expected kernel-doc formatting * Fix some kernel-doc misdemeanours * Fix expected kernel-doc formatting * Use mtd->oops_panic_write as condition SPI-NAND core: * Creation of a SPI-NAND on-die ECC engine * Move ECC related definitions earlier in the driver * Fix typo in comment * Fill a default ECC provider/algorithm * Remove outdated comment * Fix OOB read * Allow the case where there is no ECC engine * Use the external ECC engine logic SPI-NAND chip drivers: * Micron: - Add support for MT29F2G01AAAED - Use more specific names * Macronix: - Add support for MX35LFxG24AD - Add support for MX35LFxGE4AD * Toshiba: Demote non-conformant kernel-doc header SPI-NOR core: * Initial support for stateful Octal DTR mode using volatile settings * Preliminary support for JEDEC 251 (xSPI) and JEDEC 216D standards * Support for Cypress Semper flash * Support to specify ECC block size of SPI NOR flashes * Fixes to avoid clearing of non-volatile Block Protection bits at probe * hisi-sfc: Demote non-conformant kernel-doc -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAl/aS1sACgkQJWrqGEe9 VoTOqQgAiu3XfM7iHvFDiz0SVL+RFzqi2jnwXHgGDATTq+vOPwAMaqnqF6xZZZLA BIKsLwVRJnZ9Vu6Xl2vAPaVob+QKbsvvP9kkk9H/dZJ6IW4XsWFqYotPSQQ/6ZBO 2bmw9nQ0ZcksyUkdASGHuYlW/H5DAtQNQuQxGF5ywlZMxTEnD0wxUD5tccf1o3xk UYvQsQ0MNMriWCxbcdUXUmDOE9DuPdDysuLDPJs0WLnlNGgwZ/mnLvSRm6wm4nRT Y/pB6VcTMMEYAsujdf89LjCHlfCQuH5Zls9pxic8GkHjOcEqUeMLLXkkbQ1+61AO 93QsOhKAsju49/aHpbpvwu5SEmLojA== =1fie -----END PGP SIGNATURE----- Merge tag 'mtd/for-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux Pull MTD updates from Miquel Raynal: "MTD core: - Fix refcounting for unpartitioned MTDs - Fix misspelled function parameter 'section' - Remove unneeded break - cmdline parser: Fix parsing of part-names with colons - mtdpart: Fix misdocumented function parameter 'mtd' MTD devices: - phram: - Allow the user to set the erase page size - File headers are not good candidates for kernel-doc - physmap-bt1-rom: Fix __iomem addrspace removal warning - plat-ram: correctly free memory on error path in platram_probe() - powernv_flash: Add function names to headers and fix 'dev' - docg3: Fix kernel-doc 'bad line' and 'excessive doc' issues UBI cleanup fixes: - gluebi: Fix misnamed function parameter documentation - wl: Fix a couple of kernel-doc issues - eba: Fix a couple of misdocumentation issues - kapi: Correct documentation for 'ubi_leb_read_sg's 'sgl' parameter - Document 'ubi_num' in struct mtd_dev_param Generic NAND core ECC management: - Add an I/O request tweaking mechanism - Entire rework of the software BCH ECC driver, creation of a real ECC engine, getting rid of raw NAND structures, migration to more generic prototypes, misc fixes and style cleanup. Moved now to the Generic NAND layer. - Entire rework of the software Hamming ECC driver, creation of a real ECC engine, getting rid of raw NAND structures, misc renames, comment updates, cleanup, and style fixes. Moved now to the generic NAND layer. - Necessary plumbing at the NAND level to retrieve generic NAND ECC engines (softwares and on-die). - Update of the bindings. Raw NAND core: - Geting rid of the chip->ecc.priv entry. - Fix miscellaneous typos in kernel-doc Raw NAND controller drivers: - Arasan: Document 'anfc_op's 'buf' member - AU1550: Ensure the presence of the right includes - Brcmnand: Demote non-conformant kernel-doc headers - Cafe: Remove superfluous param doc and add another - Davinci: Do not use extra dereferencing - Diskonchip: Marking unused variables as __always_unused - GPMI: - Fix the driver only sense CS0 R/B issue - Fix the random DMA timeout issue - Use a single line for of_device_id - Use of_device_get_match_data() - Fix reference count leak in gpmi ops - Cleanup makefile - Fix binding matching of clocks on different SoCs - Ingenic: remove redundant get_device() in ingenic_ecc_get() - Intel LGM: New NAND controller driver - Marvell: Drop useless line - Meson: - Fix a resource leak in init - Fix meson_nfc_dma_buffer_release() arguments - mxc: - Use device_get_match_data() - Use a single line for of_device_id - Remove platform data support - Omap: - Fix a bunch of kernel-doc misdemeanours - Finish ELM half populated function header, demote empty ones - s3c2410: Add documentation for 2 missing struct members - Sunxi: Document 'sunxi_nfc's 'caps' member - Qcom: - Add support for SDX55 - Support for IPQ6018 QPIC NAND controller - Fix DMA sync on FLASH_STATUS register read - Rockchip: New NAND controller driver for RK3308, RK2928 and others - Sunxi: Add MDMA support ONENAND: - bbt: Fix expected kernel-doc formatting - Fix some kernel-doc misdemeanours - Fix expected kernel-doc formatting - Use mtd->oops_panic_write as condition SPI-NAND core: - Creation of a SPI-NAND on-die ECC engine - Move ECC related definitions earlier in the driver - Fix typo in comment - Fill a default ECC provider/algorithm - Remove outdated comment - Fix OOB read - Allow the case where there is no ECC engine - Use the external ECC engine logic SPI-NAND chip drivers: - Micron: - Add support for MT29F2G01AAAED - Use more specific names - Macronix: - Add support for MX35LFxG24AD - Add support for MX35LFxGE4AD - Toshiba: Demote non-conformant kernel-doc header SPI-NOR core: - Initial support for stateful Octal DTR mode using volatile settings - Preliminary support for JEDEC 251 (xSPI) and JEDEC 216D standards - Support for Cypress Semper flash - Support to specify ECC block size of SPI NOR flashes - Fixes to avoid clearing of non-volatile Block Protection bits at probe - hisi-sfc: Demote non-conformant kernel-doc" * tag 'mtd/for-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (120 commits) mtd: spinand: macronix: Add support for MX35LFxG24AD mtd: rawnand: rockchip: NFC driver for RK3308, RK2928 and others dt-bindings: mtd: Describe Rockchip RK3xxx NAND flash controller mtd: rawnand: gpmi: Use a single line for of_device_id mtd: rawnand: gpmi: Fix the random DMA timeout issue mtd: rawnand: gpmi: Fix the driver only sense CS0 R/B issue mtd: rawnand: qcom: Add NAND controller support for SDX55 dt-bindings: qcom_nandc: Add SDX55 QPIC NAND documentation mtd: rawnand: mxc: Use a single line for of_device_id mtd: rawnand: mxc: Use device_get_match_data() mtd: rawnand: meson: Fix a resource leak in init mtd: rawnand: gpmi: Use of_device_get_match_data() mtd: rawnand: Add NAND controller support on Intel LGM SoC dt-bindings: mtd: Add Nand Flash Controller support for Intel LGM SoC mtd: spinand: micron: Add support for MT29F2G01AAAED mtd: spinand: micron: Use more specific names mtd: rawnand: gpmi: fix reference count leak in gpmi ops dt-bindings: mtd: gpmi-nand: Fix matching of clocks on different SoCs mtd: spinand: macronix: Add support for MX35LFxGE4AD mtd: plat-ram: correctly free memory on error path in platram_probe() ...
This commit is contained in:
commit
a701262c02
|
@ -9,9 +9,6 @@ title: Freescale General-Purpose Media Interface (GPMI) binding
|
|||
maintainers:
|
||||
- Han Xu <han.xu@nxp.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "nand-controller.yaml"
|
||||
|
||||
description: |
|
||||
The GPMI nand controller provides an interface to control the NAND
|
||||
flash chips. The device tree may optionally contain sub-nodes
|
||||
|
@ -58,22 +55,10 @@ properties:
|
|||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 5
|
||||
items:
|
||||
- description: SoC gpmi io clock
|
||||
- description: SoC gpmi apb clock
|
||||
- description: SoC gpmi bch clock
|
||||
- description: SoC gpmi bch apb clock
|
||||
- description: SoC per1 bch clock
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 5
|
||||
items:
|
||||
- const: gpmi_io
|
||||
- const: gpmi_apb
|
||||
- const: gpmi_bch
|
||||
- const: gpmi_bch_apb
|
||||
- const: per1_bch
|
||||
|
||||
fsl,use-minimum-ecc:
|
||||
type: boolean
|
||||
|
@ -107,6 +92,67 @@ required:
|
|||
|
||||
unevaluatedProperties: false
|
||||
|
||||
allOf:
|
||||
- $ref: "nand-controller.yaml"
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx23-gpmi-nand
|
||||
- fsl,imx28-gpmi-nand
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: SoC gpmi io clock
|
||||
clock-names:
|
||||
items:
|
||||
- const: gpmi_io
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- fsl,imx6q-gpmi-nand
|
||||
- fsl,imx6sx-gpmi-nand
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: SoC gpmi io clock
|
||||
- description: SoC gpmi apb clock
|
||||
- description: SoC gpmi bch clock
|
||||
- description: SoC gpmi bch apb clock
|
||||
- description: SoC per1 bch clock
|
||||
clock-names:
|
||||
items:
|
||||
- const: gpmi_io
|
||||
- const: gpmi_apb
|
||||
- const: gpmi_bch
|
||||
- const: gpmi_bch_apb
|
||||
- const: per1_bch
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: fsl,imx7d-gpmi-nand
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: SoC gpmi io clock
|
||||
- description: SoC gpmi bch apb clock
|
||||
clock-names:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
items:
|
||||
- const: gpmi_io
|
||||
- const: gpmi_bch_apb
|
||||
|
||||
examples:
|
||||
- |
|
||||
nand-controller@8000c000 {
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/intel,lgm-nand.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel LGM SoC NAND Controller Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: "nand-controller.yaml"
|
||||
|
||||
maintainers:
|
||||
- Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,lgm-nand
|
||||
|
||||
reg:
|
||||
maxItems: 6
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: ebunand
|
||||
- const: hsnand
|
||||
- const: nand_cs0
|
||||
- const: nand_cs1
|
||||
- const: addr_sel0
|
||||
- const: addr_sel1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
maxItems: 2
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]+$":
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
|
||||
nand-ecc-mode: true
|
||||
|
||||
nand-ecc-algo:
|
||||
const: hw
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- clocks
|
||||
- dmas
|
||||
- dma-names
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
nand-controller@e0f00000 {
|
||||
compatible = "intel,lgm-nand";
|
||||
reg = <0xe0f00000 0x100>,
|
||||
<0xe1000000 0x300>,
|
||||
<0xe1400000 0x8000>,
|
||||
<0xe1c00000 0x1000>,
|
||||
<0x17400000 0x4>,
|
||||
<0x17c00000 0x4>;
|
||||
reg-names = "ebunand", "hsnand", "nand_cs0", "nand_cs1",
|
||||
"addr_sel0", "addr_sel1";
|
||||
clocks = <&cgu0 125>;
|
||||
dmas = <&dma0 8>, <&dma0 9>;
|
||||
dma-names = "tx", "rx";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-ecc-mode = "hw";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -46,15 +46,6 @@ patternProperties:
|
|||
description:
|
||||
Contains the native Ready/Busy IDs.
|
||||
|
||||
nand-ecc-mode:
|
||||
description:
|
||||
Desired ECC engine, either hardware (most of the time
|
||||
embedded in the NAND controller) or software correction
|
||||
(Linux will handle the calculations). soft_bch is deprecated
|
||||
and should be replaced by soft and nand-ecc-algo.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [none, soft, hw, hw_syndrome, hw_oob_first, on-die]
|
||||
|
||||
nand-ecc-engine:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
@ -171,7 +162,7 @@ examples:
|
|||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-ecc-mode = "soft";
|
||||
nand-use-soft-ecc-engine;
|
||||
nand-ecc-algo = "bch";
|
||||
|
||||
/* controller specific properties */
|
||||
|
|
|
@ -6,8 +6,12 @@ Required properties:
|
|||
SoC and it uses ADM DMA
|
||||
* "qcom,ipq4019-nand" - for QPIC NAND controller v1.4.0 being used in
|
||||
IPQ4019 SoC and it uses BAM DMA
|
||||
* "qcom,ipq6018-nand" - for QPIC NAND controller v1.5.0 being used in
|
||||
IPQ6018 SoC and it uses BAM DMA
|
||||
* "qcom,ipq8074-nand" - for QPIC NAND controller v1.5.0 being used in
|
||||
IPQ8074 SoC and it uses BAM DMA
|
||||
* "qcom,sdx55-nand" - for QPIC NAND controller v2.0.0 being used in
|
||||
SDX55 SoC and it uses BAM DMA
|
||||
|
||||
- reg: MMIO address range
|
||||
- clocks: must contain core clock and always on clock
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/rockchip,nand-controller.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Rockchip SoCs NAND FLASH Controller (NFC)
|
||||
|
||||
allOf:
|
||||
- $ref: "nand-controller.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Heiko Stuebner <heiko@sntech.de>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: rockchip,px30-nfc
|
||||
- const: rockchip,rk2928-nfc
|
||||
- const: rockchip,rv1108-nfc
|
||||
- items:
|
||||
- const: rockchip,rk3036-nfc
|
||||
- const: rockchip,rk2928-nfc
|
||||
- items:
|
||||
- const: rockchip,rk3308-nfc
|
||||
- const: rockchip,rv1108-nfc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: ahb
|
||||
- const: nfc
|
||||
|
||||
assigned-clocks:
|
||||
maxItems: 1
|
||||
|
||||
assigned-clock-rates:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
patternProperties:
|
||||
"^nand@[0-7]$":
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
|
||||
nand-ecc-mode:
|
||||
const: hw
|
||||
|
||||
nand-ecc-step-size:
|
||||
const: 1024
|
||||
|
||||
nand-ecc-strength:
|
||||
enum: [16, 24, 40, 60, 70]
|
||||
description: |
|
||||
The ECC configurations that can be supported are as follows.
|
||||
NFC v600 ECC 16, 24, 40, 60
|
||||
RK2928, RK3066, RK3188
|
||||
|
||||
NFC v622 ECC 16, 24, 40, 60
|
||||
RK3036, RK3128
|
||||
|
||||
NFC v800 ECC 16
|
||||
RK3308, RV1108
|
||||
|
||||
NFC v900 ECC 16, 40, 60, 70
|
||||
RK3326, PX30
|
||||
|
||||
nand-bus-width:
|
||||
const: 8
|
||||
|
||||
rockchip,boot-blks:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 2
|
||||
default: 16
|
||||
description:
|
||||
The NFC driver need this information to select ECC
|
||||
algorithms supported by the boot ROM.
|
||||
Only used in combination with 'nand-is-boot-medium'.
|
||||
|
||||
rockchip,boot-ecc-strength:
|
||||
enum: [16, 24, 40, 60, 70]
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
If specified it indicates that a different BCH/ECC setting is
|
||||
supported by the boot ROM.
|
||||
NFC v600 ECC 16, 24
|
||||
RK2928, RK3066, RK3188
|
||||
|
||||
NFC v622 ECC 16, 24, 40, 60
|
||||
RK3036, RK3128
|
||||
|
||||
NFC v800 ECC 16
|
||||
RK3308, RV1108
|
||||
|
||||
NFC v900 ECC 16, 70
|
||||
RK3326, PX30
|
||||
|
||||
Only used in combination with 'nand-is-boot-medium'.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/rk3308-cru.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
nfc: nand-controller@ff4b0000 {
|
||||
compatible = "rockchip,rk3308-nfc",
|
||||
"rockchip,rv1108-nfc";
|
||||
reg = <0xff4b0000 0x4000>;
|
||||
interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cru HCLK_NANDC>, <&cru SCLK_NANDC>;
|
||||
clock-names = "ahb", "nfc";
|
||||
assigned-clocks = <&clks SCLK_NANDC>;
|
||||
assigned-clock-rates = <150000000>;
|
||||
|
||||
pinctrl-0 = <&flash_ale &flash_bus8 &flash_cle &flash_csn0
|
||||
&flash_rdn &flash_rdy &flash_wrn>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
label = "rk-nand";
|
||||
nand-bus-width = <8>;
|
||||
nand-ecc-mode = "hw";
|
||||
nand-ecc-step-size = <1024>;
|
||||
nand-ecc-strength = <16>;
|
||||
nand-is-boot-medium;
|
||||
rockchip,boot-blks = <8>;
|
||||
rockchip,boot-ecc-strength = <16>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -5,7 +5,7 @@ NAND Error-correction Code
|
|||
Introduction
|
||||
============
|
||||
|
||||
Having looked at the linux mtd/nand driver and more specific at nand_ecc.c
|
||||
Having looked at the linux mtd/nand Hamming software ECC engine driver
|
||||
I felt there was room for optimisation. I bashed the code for a few hours
|
||||
performing tricks like table lookup removing superfluous code etc.
|
||||
After that the speed was increased by 35-40%.
|
||||
|
|
|
@ -972,9 +972,6 @@ hints" for an explanation.
|
|||
.. kernel-doc:: drivers/mtd/nand/raw/nand_base.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: drivers/mtd/nand/raw/nand_ecc.c
|
||||
:export:
|
||||
|
||||
Internal Functions Provided
|
||||
===========================
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <net/ax88796.h>
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include "devs.h"
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <linux/platform_data/asoc-s3c24xx_simtec.h>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include "gpio-cfg.h"
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include "gpio-cfg.h"
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include "cpu.h"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/memblock.h>
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ config SM_FTL
|
|||
tristate "SmartMedia/xD new translation layer"
|
||||
depends on BLOCK
|
||||
select MTD_BLKDEVS
|
||||
select MTD_NAND_CORE
|
||||
select MTD_NAND_ECC_SW_HAMMING
|
||||
help
|
||||
This enables EXPERIMENTAL R/W support for SmartMedia/xD
|
||||
|
|
|
@ -816,7 +816,7 @@ static void doc_read_page_finish(struct docg3 *docg3)
|
|||
|
||||
/**
|
||||
* calc_block_sector - Calculate blocks, pages and ofs.
|
||||
|
||||
*
|
||||
* @from: offset in flash
|
||||
* @block0: first plane block index calculated
|
||||
* @block1: second plane block index calculated
|
||||
|
@ -1783,10 +1783,9 @@ static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* doc_probe_device - Check if a device is available
|
||||
* @base: the io space where the device is probed
|
||||
* @cascade: the cascade of chips this devices will belong to
|
||||
* @floor: the floor of the probed device
|
||||
* @dev: the device
|
||||
* @cascade: the cascade of chips this devices will belong to
|
||||
*
|
||||
* Checks whether a device at the specified IO range, and floor is available.
|
||||
*
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) ???? Jochen Schäuble <psionic@psionic.de>
|
||||
* Copyright (c) 2003-2004 Joern Engel <joern@wh.fh-wedel.de>
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* one commend line parameter per device, each in the form:
|
||||
* phram=<name>,<start>,<len>
|
||||
* phram=<name>,<start>,<len>[,<erasesize>]
|
||||
* <name> may be up to 63 characters.
|
||||
* <start> and <len> can be octal, decimal or hexadecimal. If followed
|
||||
* <start>, <len>, and <erasesize> can be octal, decimal or hexadecimal. If followed
|
||||
* by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or
|
||||
* gigabytes.
|
||||
* gigabytes. <erasesize> is optional and defaults to PAGE_SIZE.
|
||||
*
|
||||
* Example:
|
||||
* phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
|
||||
* phram=swap,64Mi,128Mi phram=test,900Mi,1Mi,64Ki
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
@ -26,6 +26,7 @@
|
|||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <asm/div64.h>
|
||||
|
||||
struct phram_mtd_list {
|
||||
struct mtd_info mtd;
|
||||
|
@ -88,7 +89,7 @@ static void unregister_devices(void)
|
|||
}
|
||||
}
|
||||
|
||||
static int register_device(char *name, phys_addr_t start, size_t len)
|
||||
static int register_device(char *name, phys_addr_t start, size_t len, uint32_t erasesize)
|
||||
{
|
||||
struct phram_mtd_list *new;
|
||||
int ret = -ENOMEM;
|
||||
|
@ -115,7 +116,7 @@ static int register_device(char *name, phys_addr_t start, size_t len)
|
|||
new->mtd._write = phram_write;
|
||||
new->mtd.owner = THIS_MODULE;
|
||||
new->mtd.type = MTD_RAM;
|
||||
new->mtd.erasesize = PAGE_SIZE;
|
||||
new->mtd.erasesize = erasesize;
|
||||
new->mtd.writesize = 1;
|
||||
|
||||
ret = -EAGAIN;
|
||||
|
@ -204,22 +205,23 @@ static inline void kill_final_newline(char *str)
|
|||
static int phram_init_called;
|
||||
/*
|
||||
* This shall contain the module parameter if any. It is of the form:
|
||||
* - phram=<device>,<address>,<size> for module case
|
||||
* - phram.phram=<device>,<address>,<size> for built-in case
|
||||
* We leave 64 bytes for the device name, 20 for the address and 20 for the
|
||||
* size.
|
||||
* Example: phram.phram=rootfs,0xa0000000,512Mi
|
||||
* - phram=<device>,<address>,<size>[,<erasesize>] for module case
|
||||
* - phram.phram=<device>,<address>,<size>[,<erasesize>] for built-in case
|
||||
* We leave 64 bytes for the device name, 20 for the address , 20 for the
|
||||
* size and 20 for the erasesize.
|
||||
* Example: phram.phram=rootfs,0xa0000000,512Mi,65536
|
||||
*/
|
||||
static char phram_paramline[64 + 20 + 20];
|
||||
static char phram_paramline[64 + 20 + 20 + 20];
|
||||
#endif
|
||||
|
||||
static int phram_setup(const char *val)
|
||||
{
|
||||
char buf[64 + 20 + 20], *str = buf;
|
||||
char *token[3];
|
||||
char buf[64 + 20 + 20 + 20], *str = buf;
|
||||
char *token[4];
|
||||
char *name;
|
||||
uint64_t start;
|
||||
uint64_t len;
|
||||
uint64_t erasesize = PAGE_SIZE;
|
||||
int i, ret;
|
||||
|
||||
if (strnlen(val, sizeof(buf)) >= sizeof(buf))
|
||||
|
@ -228,7 +230,7 @@ static int phram_setup(const char *val)
|
|||
strcpy(str, val);
|
||||
kill_final_newline(str);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
for (i = 0; i < 4; i++)
|
||||
token[i] = strsep(&str, ",");
|
||||
|
||||
if (str)
|
||||
|
@ -253,11 +255,25 @@ static int phram_setup(const char *val)
|
|||
goto error;
|
||||
}
|
||||
|
||||
ret = register_device(name, start, len);
|
||||
if (token[3]) {
|
||||
ret = parse_num64(&erasesize, token[3]);
|
||||
if (ret) {
|
||||
parse_err("illegal erasesize\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 0 || erasesize == 0 || erasesize > len
|
||||
|| erasesize > UINT_MAX || do_div(len, (uint32_t)erasesize) != 0) {
|
||||
parse_err("illegal erasesize or len\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = register_device(name, start, len, (uint32_t)erasesize);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
pr_info("%s device: %#llx at %#llx\n", name, len, start);
|
||||
pr_info("%s device: %#llx at %#llx for erasesize %#llx\n", name, len, start, erasesize);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
@ -298,7 +314,7 @@ static int phram_param_call(const char *val, const struct kernel_param *kp)
|
|||
}
|
||||
|
||||
module_param_call(phram, phram_param_call, NULL, NULL, 0200);
|
||||
MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"");
|
||||
MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>[,<erasesize>]\"");
|
||||
|
||||
|
||||
static int __init init_phram(void)
|
||||
|
|
|
@ -126,6 +126,7 @@ static int powernv_flash_async_op(struct mtd_info *mtd, enum flash_op op,
|
|||
}
|
||||
|
||||
/**
|
||||
* powernv_flash_read
|
||||
* @mtd: the device
|
||||
* @from: the offset to read from
|
||||
* @len: the number of bytes to read
|
||||
|
@ -142,6 +143,7 @@ static int powernv_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
}
|
||||
|
||||
/**
|
||||
* powernv_flash_write
|
||||
* @mtd: the device
|
||||
* @to: the offset to write to
|
||||
* @len: the number of bytes to write
|
||||
|
@ -158,6 +160,7 @@ static int powernv_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
}
|
||||
|
||||
/**
|
||||
* powernv_flash_erase
|
||||
* @mtd: the device
|
||||
* @erase: the erase info
|
||||
* Returns 0 if erase successful or -ERRNO if an error occurred
|
||||
|
@ -176,7 +179,7 @@ static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)
|
|||
|
||||
/**
|
||||
* powernv_flash_set_driver_info - Fill the mtd_info structure and docg3
|
||||
* structure @pdev: The platform device
|
||||
* @dev: The device structure
|
||||
* @mtd: The structure to fill
|
||||
*/
|
||||
static int powernv_flash_set_driver_info(struct device *dev,
|
||||
|
|
|
@ -31,12 +31,12 @@ static map_word __xipram bt1_rom_map_read(struct map_info *map,
|
|||
unsigned long ofs)
|
||||
{
|
||||
void __iomem *src = map->virt + ofs;
|
||||
unsigned long shift;
|
||||
unsigned int shift;
|
||||
map_word ret;
|
||||
u32 data;
|
||||
|
||||
/* Read data within offset dword. */
|
||||
shift = (unsigned long)src & 0x3;
|
||||
shift = (uintptr_t)src & 0x3;
|
||||
data = readl_relaxed(src - shift);
|
||||
if (!shift) {
|
||||
ret.x[0] = data;
|
||||
|
@ -60,7 +60,7 @@ static void __xipram bt1_rom_map_copy_from(struct map_info *map,
|
|||
ssize_t len)
|
||||
{
|
||||
void __iomem *src = map->virt + from;
|
||||
ssize_t shift, chunk;
|
||||
unsigned int shift, chunk;
|
||||
u32 data;
|
||||
|
||||
if (len <= 0 || from >= map->size)
|
||||
|
@ -75,7 +75,7 @@ static void __xipram bt1_rom_map_copy_from(struct map_info *map,
|
|||
* up into the next three stages: unaligned head, aligned body,
|
||||
* unaligned tail.
|
||||
*/
|
||||
shift = (ssize_t)src & 0x3;
|
||||
shift = (uintptr_t)src & 0x3;
|
||||
if (shift) {
|
||||
chunk = min_t(ssize_t, 4 - shift, len);
|
||||
data = readl_relaxed(src - shift);
|
||||
|
|
|
@ -177,8 +177,12 @@ static int platram_probe(struct platform_device *pdev)
|
|||
err = mtd_device_parse_register(info->mtd, pdata->probes, NULL,
|
||||
pdata->partitions,
|
||||
pdata->nr_partitions);
|
||||
if (!err)
|
||||
dev_info(&pdev->dev, "registered mtd device\n");
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register mtd device\n");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "registered mtd device\n");
|
||||
|
||||
if (pdata->nr_partitions) {
|
||||
/* add the whole device. */
|
||||
|
@ -186,10 +190,11 @@ static int platram_probe(struct platform_device *pdev)
|
|||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to register the entire device\n");
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
exit_free:
|
||||
platram_remove(pdev);
|
||||
|
|
|
@ -881,7 +881,6 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
|||
if (copy_from_user(&offs, argp, sizeof(loff_t)))
|
||||
return -EFAULT;
|
||||
return mtd_block_isbad(mtd, offs);
|
||||
break;
|
||||
}
|
||||
|
||||
case MEMSETBADBLOCK:
|
||||
|
@ -891,7 +890,6 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
|||
if (copy_from_user(&offs, argp, sizeof(loff_t)))
|
||||
return -EFAULT;
|
||||
return mtd_block_markbad(mtd, offs);
|
||||
break;
|
||||
}
|
||||
|
||||
case OTPSELECT:
|
||||
|
|
|
@ -993,6 +993,8 @@ int __get_mtd_device(struct mtd_info *mtd)
|
|||
}
|
||||
}
|
||||
|
||||
master->usecount++;
|
||||
|
||||
while (mtd->parent) {
|
||||
mtd->usecount++;
|
||||
mtd = mtd->parent;
|
||||
|
@ -1059,6 +1061,8 @@ void __put_mtd_device(struct mtd_info *mtd)
|
|||
mtd = mtd->parent;
|
||||
}
|
||||
|
||||
master->usecount--;
|
||||
|
||||
if (master->_put_device)
|
||||
master->_put_device(master);
|
||||
|
||||
|
@ -1578,7 +1582,7 @@ static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte,
|
|||
* ECC byte
|
||||
* @mtd: mtd info structure
|
||||
* @eccbyte: the byte we are searching for
|
||||
* @sectionp: pointer where the section id will be stored
|
||||
* @section: pointer where the section id will be stored
|
||||
* @oobregion: OOB region information
|
||||
*
|
||||
* Works like mtd_ooblayout_find_region() except it searches for a specific ECC
|
||||
|
|
|
@ -292,7 +292,7 @@ EXPORT_SYMBOL_GPL(mtd_add_partition);
|
|||
/**
|
||||
* __mtd_del_partition - delete MTD partition
|
||||
*
|
||||
* @priv: MTD structure to be deleted
|
||||
* @mtd: MTD structure to be deleted
|
||||
*
|
||||
* This function must be called with the partitions mutex locked.
|
||||
*/
|
||||
|
|
|
@ -13,7 +13,38 @@ menu "ECC engine support"
|
|||
|
||||
config MTD_NAND_ECC
|
||||
bool
|
||||
depends on MTD_NAND_CORE
|
||||
select MTD_NAND_CORE
|
||||
|
||||
config MTD_NAND_ECC_SW_HAMMING
|
||||
bool "Software Hamming ECC engine"
|
||||
default y if MTD_RAW_NAND
|
||||
select MTD_NAND_ECC
|
||||
help
|
||||
This enables support for software Hamming error
|
||||
correction. This correction can correct up to 1 bit error
|
||||
per chunk and detect up to 2 bit errors. While it used to be
|
||||
widely used with old parts, newer NAND chips usually require
|
||||
more strength correction and in this case BCH or RS will be
|
||||
preferred.
|
||||
|
||||
config MTD_NAND_ECC_SW_HAMMING_SMC
|
||||
bool "NAND ECC Smart Media byte order"
|
||||
depends on MTD_NAND_ECC_SW_HAMMING
|
||||
default n
|
||||
help
|
||||
Software ECC according to the Smart Media Specification.
|
||||
The original Linux implementation had byte 0 and 1 swapped.
|
||||
|
||||
config MTD_NAND_ECC_SW_BCH
|
||||
bool "Software BCH ECC engine"
|
||||
select BCH
|
||||
select MTD_NAND_ECC
|
||||
default n
|
||||
help
|
||||
This enables support for software BCH error correction. Binary BCH
|
||||
codes are more powerful and cpu intensive than traditional Hamming
|
||||
ECC codes. They are used with NAND devices requiring more than 1 bit
|
||||
of error correction.
|
||||
|
||||
endmenu
|
||||
|
||||
|
|
|
@ -8,3 +8,5 @@ obj-y += raw/
|
|||
obj-y += spi/
|
||||
|
||||
nandcore-$(CONFIG_MTD_NAND_ECC) += ecc.o
|
||||
nandcore-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += ecc-sw-hamming.o
|
||||
nandcore-$(CONFIG_MTD_NAND_ECC_SW_BCH) += ecc-sw-bch.o
|
||||
|
|
|
@ -207,6 +207,130 @@ int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks);
|
||||
|
||||
/**
|
||||
* nanddev_get_ecc_engine() - Find and get a suitable ECC engine
|
||||
* @nand: NAND device
|
||||
*/
|
||||
static int nanddev_get_ecc_engine(struct nand_device *nand)
|
||||
{
|
||||
int engine_type;
|
||||
|
||||
/* Read the user desires in terms of ECC engine/configuration */
|
||||
of_get_nand_ecc_user_config(nand);
|
||||
|
||||
engine_type = nand->ecc.user_conf.engine_type;
|
||||
if (engine_type == NAND_ECC_ENGINE_TYPE_INVALID)
|
||||
engine_type = nand->ecc.defaults.engine_type;
|
||||
|
||||
switch (engine_type) {
|
||||
case NAND_ECC_ENGINE_TYPE_NONE:
|
||||
return 0;
|
||||
case NAND_ECC_ENGINE_TYPE_SOFT:
|
||||
nand->ecc.engine = nand_ecc_get_sw_engine(nand);
|
||||
break;
|
||||
case NAND_ECC_ENGINE_TYPE_ON_DIE:
|
||||
nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand);
|
||||
break;
|
||||
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
||||
pr_err("On-host hardware ECC engines not supported yet\n");
|
||||
break;
|
||||
default:
|
||||
pr_err("Missing ECC engine type\n");
|
||||
}
|
||||
|
||||
if (!nand->ecc.engine)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nanddev_put_ecc_engine() - Dettach and put the in-use ECC engine
|
||||
* @nand: NAND device
|
||||
*/
|
||||
static int nanddev_put_ecc_engine(struct nand_device *nand)
|
||||
{
|
||||
switch (nand->ecc.ctx.conf.engine_type) {
|
||||
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
||||
pr_err("On-host hardware ECC engines not supported yet\n");
|
||||
break;
|
||||
case NAND_ECC_ENGINE_TYPE_NONE:
|
||||
case NAND_ECC_ENGINE_TYPE_SOFT:
|
||||
case NAND_ECC_ENGINE_TYPE_ON_DIE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nanddev_find_ecc_configuration() - Find a suitable ECC configuration
|
||||
* @nand: NAND device
|
||||
*/
|
||||
static int nanddev_find_ecc_configuration(struct nand_device *nand)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!nand->ecc.engine)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = nand_ecc_init_ctx(nand);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!nand_ecc_is_strong_enough(nand))
|
||||
pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
|
||||
nand->mtd.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nanddev_ecc_engine_init() - Initialize an ECC engine for the chip
|
||||
* @nand: NAND device
|
||||
*/
|
||||
int nanddev_ecc_engine_init(struct nand_device *nand)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Look for the ECC engine to use */
|
||||
ret = nanddev_get_ecc_engine(nand);
|
||||
if (ret) {
|
||||
pr_err("No ECC engine found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* No ECC engine requested */
|
||||
if (!nand->ecc.engine)
|
||||
return 0;
|
||||
|
||||
/* Configure the engine: balance user input and chip requirements */
|
||||
ret = nanddev_find_ecc_configuration(nand);
|
||||
if (ret) {
|
||||
pr_err("No suitable ECC configuration\n");
|
||||
nanddev_put_ecc_engine(nand);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nanddev_ecc_engine_init);
|
||||
|
||||
/**
|
||||
* nanddev_ecc_engine_cleanup() - Cleanup ECC engine initializations
|
||||
* @nand: NAND device
|
||||
*/
|
||||
void nanddev_ecc_engine_cleanup(struct nand_device *nand)
|
||||
{
|
||||
if (nand->ecc.engine)
|
||||
nand_ecc_cleanup_ctx(nand);
|
||||
|
||||
nanddev_put_ecc_engine(nand);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nanddev_ecc_engine_cleanup);
|
||||
|
||||
/**
|
||||
* nanddev_init() - Initialize a NAND device
|
||||
* @nand: NAND device
|
||||
|
|
|
@ -0,0 +1,406 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* This file provides ECC correction for more than 1 bit per block of data,
|
||||
* using binary BCH codes. It relies on the generic BCH library lib/bch.c.
|
||||
*
|
||||
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand-ecc-sw-bch.h>
|
||||
|
||||
/**
|
||||
* nand_ecc_sw_bch_calculate - Calculate the ECC corresponding to a data block
|
||||
* @nand: NAND device
|
||||
* @buf: Input buffer with raw data
|
||||
* @code: Output buffer with ECC
|
||||
*/
|
||||
int nand_ecc_sw_bch_calculate(struct nand_device *nand,
|
||||
const unsigned char *buf, unsigned char *code)
|
||||
{
|
||||
struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
unsigned int i;
|
||||
|
||||
memset(code, 0, engine_conf->code_size);
|
||||
bch_encode(engine_conf->bch, buf, nand->ecc.ctx.conf.step_size, code);
|
||||
|
||||
/* apply mask so that an erased page is a valid codeword */
|
||||
for (i = 0; i < engine_conf->code_size; i++)
|
||||
code[i] ^= engine_conf->eccmask[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_sw_bch_calculate);
|
||||
|
||||
/**
|
||||
* nand_ecc_sw_bch_correct - Detect, correct and report bit error(s)
|
||||
* @nand: NAND device
|
||||
* @buf: Raw data read from the chip
|
||||
* @read_ecc: ECC bytes from the chip
|
||||
* @calc_ecc: ECC calculated from the raw data
|
||||
*
|
||||
* Detect and correct bit errors for a data block.
|
||||
*/
|
||||
int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
{
|
||||
struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
unsigned int step_size = nand->ecc.ctx.conf.step_size;
|
||||
unsigned int *errloc = engine_conf->errloc;
|
||||
int i, count;
|
||||
|
||||
count = bch_decode(engine_conf->bch, NULL, step_size, read_ecc,
|
||||
calc_ecc, NULL, errloc);
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
if (errloc[i] < (step_size * 8))
|
||||
/* The error is in the data area: correct it */
|
||||
buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
|
||||
|
||||
/* Otherwise the error is in the ECC area: nothing to do */
|
||||
pr_debug("%s: corrected bitflip %u\n", __func__,
|
||||
errloc[i]);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
pr_err("ECC unrecoverable error\n");
|
||||
count = -EBADMSG;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_sw_bch_correct);
|
||||
|
||||
/**
|
||||
* nand_ecc_sw_bch_cleanup - Cleanup software BCH ECC resources
|
||||
* @nand: NAND device
|
||||
*/
|
||||
static void nand_ecc_sw_bch_cleanup(struct nand_device *nand)
|
||||
{
|
||||
struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
|
||||
bch_free(engine_conf->bch);
|
||||
kfree(engine_conf->errloc);
|
||||
kfree(engine_conf->eccmask);
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_ecc_sw_bch_init - Initialize software BCH ECC engine
|
||||
* @nand: NAND device
|
||||
*
|
||||
* Returns: a pointer to a new NAND BCH control structure, or NULL upon failure
|
||||
*
|
||||
* Initialize NAND BCH error correction. @nand.ecc parameters 'step_size' and
|
||||
* 'bytes' are used to compute the following BCH parameters:
|
||||
* m, the Galois field order
|
||||
* t, the error correction capability
|
||||
* 'bytes' should be equal to the number of bytes required to store m * t
|
||||
* bits, where m is such that 2^m - 1 > step_size * 8.
|
||||
*
|
||||
* Example: to configure 4 bit correction per 512 bytes, you should pass
|
||||
* step_size = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8)
|
||||
* bytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits)
|
||||
*/
|
||||
static int nand_ecc_sw_bch_init(struct nand_device *nand)
|
||||
{
|
||||
struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
unsigned int eccsize = nand->ecc.ctx.conf.step_size;
|
||||
unsigned int eccbytes = engine_conf->code_size;
|
||||
unsigned int m, t, i;
|
||||
unsigned char *erased_page;
|
||||
int ret;
|
||||
|
||||
m = fls(1 + (8 * eccsize));
|
||||
t = (eccbytes * 8) / m;
|
||||
|
||||
engine_conf->bch = bch_init(m, t, 0, false);
|
||||
if (!engine_conf->bch)
|
||||
return -EINVAL;
|
||||
|
||||
engine_conf->eccmask = kzalloc(eccbytes, GFP_KERNEL);
|
||||
engine_conf->errloc = kmalloc_array(t, sizeof(*engine_conf->errloc),
|
||||
GFP_KERNEL);
|
||||
if (!engine_conf->eccmask || !engine_conf->errloc) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Compute and store the inverted ECC of an erased step */
|
||||
erased_page = kmalloc(eccsize, GFP_KERNEL);
|
||||
if (!erased_page) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memset(erased_page, 0xff, eccsize);
|
||||
bch_encode(engine_conf->bch, erased_page, eccsize,
|
||||
engine_conf->eccmask);
|
||||
kfree(erased_page);
|
||||
|
||||
for (i = 0; i < eccbytes; i++)
|
||||
engine_conf->eccmask[i] ^= 0xff;
|
||||
|
||||
/* Verify that the number of code bytes has the expected value */
|
||||
if (engine_conf->bch->ecc_bytes != eccbytes) {
|
||||
pr_err("Invalid number of ECC bytes: %u, expected: %u\n",
|
||||
eccbytes, engine_conf->bch->ecc_bytes);
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Sanity checks */
|
||||
if (8 * (eccsize + eccbytes) >= (1 << m)) {
|
||||
pr_err("ECC step size is too large (%u)\n", eccsize);
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
nand_ecc_sw_bch_cleanup(nand);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
|
||||
{
|
||||
struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
struct nand_ecc_sw_bch_conf *engine_conf;
|
||||
unsigned int code_size = 0, nsteps;
|
||||
int ret;
|
||||
|
||||
/* Only large page NAND chips may use BCH */
|
||||
if (mtd->oobsize < 64) {
|
||||
pr_err("BCH cannot be used with small page NAND chips\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!mtd->ooblayout)
|
||||
mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout());
|
||||
|
||||
conf->engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
|
||||
conf->algo = NAND_ECC_ALGO_BCH;
|
||||
conf->step_size = nand->ecc.user_conf.step_size;
|
||||
conf->strength = nand->ecc.user_conf.strength;
|
||||
|
||||
/*
|
||||
* Board driver should supply ECC size and ECC strength
|
||||
* values to select how many bits are correctable.
|
||||
* Otherwise, default to 512 bytes for large page devices and 256 for
|
||||
* small page devices.
|
||||
*/
|
||||
if (!conf->step_size) {
|
||||
if (mtd->oobsize >= 64)
|
||||
conf->step_size = 512;
|
||||
else
|
||||
conf->step_size = 256;
|
||||
|
||||
conf->strength = 4;
|
||||
}
|
||||
|
||||
nsteps = mtd->writesize / conf->step_size;
|
||||
|
||||
/* Maximize */
|
||||
if (nand->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
|
||||
conf->step_size = 1024;
|
||||
nsteps = mtd->writesize / conf->step_size;
|
||||
/* Reserve 2 bytes for the BBM */
|
||||
code_size = (mtd->oobsize - 2) / nsteps;
|
||||
conf->strength = code_size * 8 / fls(8 * conf->step_size);
|
||||
}
|
||||
|
||||
if (!code_size)
|
||||
code_size = DIV_ROUND_UP(conf->strength *
|
||||
fls(8 * conf->step_size), 8);
|
||||
|
||||
if (!conf->strength)
|
||||
conf->strength = (code_size * 8) / fls(8 * conf->step_size);
|
||||
|
||||
if (!code_size && !conf->strength) {
|
||||
pr_err("Missing ECC parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
|
||||
if (!engine_conf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = nand_ecc_init_req_tweaking(&engine_conf->req_ctx, nand);
|
||||
if (ret)
|
||||
goto free_engine_conf;
|
||||
|
||||
engine_conf->code_size = code_size;
|
||||
engine_conf->nsteps = nsteps;
|
||||
engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
|
||||
engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
|
||||
if (!engine_conf->calc_buf || !engine_conf->code_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto free_bufs;
|
||||
}
|
||||
|
||||
nand->ecc.ctx.priv = engine_conf;
|
||||
nand->ecc.ctx.total = nsteps * code_size;
|
||||
|
||||
ret = nand_ecc_sw_bch_init(nand);
|
||||
if (ret)
|
||||
goto free_bufs;
|
||||
|
||||
/* Verify the layout validity */
|
||||
if (mtd_ooblayout_count_eccbytes(mtd) !=
|
||||
engine_conf->nsteps * engine_conf->code_size) {
|
||||
pr_err("Invalid ECC layout\n");
|
||||
ret = -EINVAL;
|
||||
goto cleanup_bch_ctx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_bch_ctx:
|
||||
nand_ecc_sw_bch_cleanup(nand);
|
||||
free_bufs:
|
||||
nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
|
||||
kfree(engine_conf->calc_buf);
|
||||
kfree(engine_conf->code_buf);
|
||||
free_engine_conf:
|
||||
kfree(engine_conf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_sw_bch_init_ctx);
|
||||
|
||||
void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand)
|
||||
{
|
||||
struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
|
||||
if (engine_conf) {
|
||||
nand_ecc_sw_bch_cleanup(nand);
|
||||
nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
|
||||
kfree(engine_conf->calc_buf);
|
||||
kfree(engine_conf->code_buf);
|
||||
kfree(engine_conf);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup_ctx);
|
||||
|
||||
static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
int eccsize = nand->ecc.ctx.conf.step_size;
|
||||
int eccbytes = engine_conf->code_size;
|
||||
int eccsteps = engine_conf->nsteps;
|
||||
int total = nand->ecc.ctx.total;
|
||||
u8 *ecccalc = engine_conf->calc_buf;
|
||||
const u8 *data;
|
||||
int i;
|
||||
|
||||
/* Nothing to do for a raw operation */
|
||||
if (req->mode == MTD_OPS_RAW)
|
||||
return 0;
|
||||
|
||||
/* This engine does not provide BBM/free OOB bytes protection */
|
||||
if (!req->datalen)
|
||||
return 0;
|
||||
|
||||
nand_ecc_tweak_req(&engine_conf->req_ctx, req);
|
||||
|
||||
/* No more preparation for page read */
|
||||
if (req->type == NAND_PAGE_READ)
|
||||
return 0;
|
||||
|
||||
/* Preparation for page write: derive the ECC bytes and place them */
|
||||
for (i = 0, data = req->databuf.out;
|
||||
eccsteps;
|
||||
eccsteps--, i += eccbytes, data += eccsize)
|
||||
nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]);
|
||||
|
||||
return mtd_ooblayout_set_eccbytes(mtd, ecccalc, (void *)req->oobbuf.out,
|
||||
0, total);
|
||||
}
|
||||
|
||||
static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
int eccsize = nand->ecc.ctx.conf.step_size;
|
||||
int total = nand->ecc.ctx.total;
|
||||
int eccbytes = engine_conf->code_size;
|
||||
int eccsteps = engine_conf->nsteps;
|
||||
u8 *ecccalc = engine_conf->calc_buf;
|
||||
u8 *ecccode = engine_conf->code_buf;
|
||||
unsigned int max_bitflips = 0;
|
||||
u8 *data = req->databuf.in;
|
||||
int i, ret;
|
||||
|
||||
/* Nothing to do for a raw operation */
|
||||
if (req->mode == MTD_OPS_RAW)
|
||||
return 0;
|
||||
|
||||
/* This engine does not provide BBM/free OOB bytes protection */
|
||||
if (!req->datalen)
|
||||
return 0;
|
||||
|
||||
/* No more preparation for page write */
|
||||
if (req->type == NAND_PAGE_WRITE) {
|
||||
nand_ecc_restore_req(&engine_conf->req_ctx, req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Finish a page read: retrieve the (raw) ECC bytes*/
|
||||
ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, req->oobbuf.in, 0,
|
||||
total);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Calculate the ECC bytes */
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize)
|
||||
nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]);
|
||||
|
||||
/* Finish a page read: compare and correct */
|
||||
for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in;
|
||||
eccsteps;
|
||||
eccsteps--, i += eccbytes, data += eccsize) {
|
||||
int stat = nand_ecc_sw_bch_correct(nand, data,
|
||||
&ecccode[i],
|
||||
&ecccalc[i]);
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
|
||||
nand_ecc_restore_req(&engine_conf->req_ctx, req);
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
static struct nand_ecc_engine_ops nand_ecc_sw_bch_engine_ops = {
|
||||
.init_ctx = nand_ecc_sw_bch_init_ctx,
|
||||
.cleanup_ctx = nand_ecc_sw_bch_cleanup_ctx,
|
||||
.prepare_io_req = nand_ecc_sw_bch_prepare_io_req,
|
||||
.finish_io_req = nand_ecc_sw_bch_finish_io_req,
|
||||
};
|
||||
|
||||
static struct nand_ecc_engine nand_ecc_sw_bch_engine = {
|
||||
.ops = &nand_ecc_sw_bch_engine_ops,
|
||||
};
|
||||
|
||||
struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void)
|
||||
{
|
||||
return &nand_ecc_sw_bch_engine;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_sw_bch_get_engine);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
|
||||
MODULE_DESCRIPTION("NAND software BCH ECC support");
|
|
@ -17,9 +17,9 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/*
|
||||
|
@ -75,7 +75,7 @@ static const char bitsperbyte[256] = {
|
|||
* addressbits is a lookup table to filter out the bits from the xor-ed
|
||||
* ECC data that identify the faulty location.
|
||||
* this is only used for repairing parity
|
||||
* see the comments in nand_correct_data for more details
|
||||
* see the comments in nand_ecc_sw_hamming_correct for more details
|
||||
*/
|
||||
static const char addressbits[256] = {
|
||||
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
|
||||
|
@ -112,30 +112,21 @@ static const char addressbits[256] = {
|
|||
0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f
|
||||
};
|
||||
|
||||
/**
|
||||
* __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
|
||||
* block
|
||||
* @buf: input buffer with raw data
|
||||
* @eccsize: data bytes per ECC step (256 or 512)
|
||||
* @code: output buffer with ECC
|
||||
* @sm_order: Smart Media byte ordering
|
||||
*/
|
||||
void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
|
||||
unsigned char *code, bool sm_order)
|
||||
int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size,
|
||||
unsigned char *code, bool sm_order)
|
||||
{
|
||||
const u32 *bp = (uint32_t *)buf;
|
||||
const u32 eccsize_mult = (step_size == 256) ? 1 : 2;
|
||||
/* current value in buffer */
|
||||
u32 cur;
|
||||
/* rp0..rp17 are the various accumulated parities (per byte) */
|
||||
u32 rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9, rp10, rp11, rp12,
|
||||
rp13, rp14, rp15, rp16, rp17;
|
||||
/* Cumulative parity for all data */
|
||||
u32 par;
|
||||
/* Cumulative parity at the end of the loop (rp12, rp14, rp16) */
|
||||
u32 tmppar;
|
||||
int i;
|
||||
const uint32_t *bp = (uint32_t *)buf;
|
||||
/* 256 or 512 bytes/ecc */
|
||||
const uint32_t eccsize_mult = eccsize >> 8;
|
||||
uint32_t cur; /* current value in buffer */
|
||||
/* rp0..rp15..rp17 are the various accumulated parities (per byte) */
|
||||
uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7;
|
||||
uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16;
|
||||
uint32_t rp17;
|
||||
uint32_t par; /* the cumulative parity for all data */
|
||||
uint32_t tmppar; /* the cumulative parity for this iteration;
|
||||
for rp12, rp14 and rp16 at the end of the
|
||||
loop */
|
||||
|
||||
par = 0;
|
||||
rp4 = 0;
|
||||
|
@ -145,6 +136,7 @@ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
|
|||
rp12 = 0;
|
||||
rp14 = 0;
|
||||
rp16 = 0;
|
||||
rp17 = 0;
|
||||
|
||||
/*
|
||||
* The loop is unrolled a number of times;
|
||||
|
@ -356,45 +348,35 @@ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
|
|||
(invparity[par & 0x55] << 2) |
|
||||
(invparity[rp17] << 1) |
|
||||
(invparity[rp16] << 0);
|
||||
}
|
||||
EXPORT_SYMBOL(__nand_calculate_ecc);
|
||||
|
||||
/**
|
||||
* nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
|
||||
* block
|
||||
* @chip: NAND chip object
|
||||
* @buf: input buffer with raw data
|
||||
* @code: output buffer with ECC
|
||||
*/
|
||||
int nand_calculate_ecc(struct nand_chip *chip, const unsigned char *buf,
|
||||
unsigned char *code)
|
||||
{
|
||||
bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER;
|
||||
|
||||
__nand_calculate_ecc(buf, chip->ecc.size, code, sm_order);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_calculate_ecc);
|
||||
EXPORT_SYMBOL(ecc_sw_hamming_calculate);
|
||||
|
||||
/**
|
||||
* __nand_correct_data - [NAND Interface] Detect and correct bit error(s)
|
||||
* @buf: raw data read from the chip
|
||||
* @read_ecc: ECC from the chip
|
||||
* @calc_ecc: the ECC calculated from raw data
|
||||
* @eccsize: data bytes per ECC step (256 or 512)
|
||||
* @sm_order: Smart Media byte order
|
||||
*
|
||||
* Detect and correct a 1 bit error for eccsize byte block
|
||||
* nand_ecc_sw_hamming_calculate - Calculate 3-byte ECC for 256/512-byte block
|
||||
* @nand: NAND device
|
||||
* @buf: Input buffer with raw data
|
||||
* @code: Output buffer with ECC
|
||||
*/
|
||||
int __nand_correct_data(unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc,
|
||||
unsigned int eccsize, bool sm_order)
|
||||
int nand_ecc_sw_hamming_calculate(struct nand_device *nand,
|
||||
const unsigned char *buf, unsigned char *code)
|
||||
{
|
||||
struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
unsigned int step_size = nand->ecc.ctx.conf.step_size;
|
||||
|
||||
return ecc_sw_hamming_calculate(buf, step_size, code,
|
||||
engine_conf->sm_order);
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_sw_hamming_calculate);
|
||||
|
||||
int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc,
|
||||
unsigned char *calc_ecc, unsigned int step_size,
|
||||
bool sm_order)
|
||||
{
|
||||
const u32 eccsize_mult = step_size >> 8;
|
||||
unsigned char b0, b1, b2, bit_addr;
|
||||
unsigned int byte_addr;
|
||||
/* 256 or 512 bytes/ecc */
|
||||
const uint32_t eccsize_mult = eccsize >> 8;
|
||||
|
||||
/*
|
||||
* b0 to b2 indicate which bit is faulty (if any)
|
||||
|
@ -458,27 +440,220 @@ int __nand_correct_data(unsigned char *buf,
|
|||
pr_err("%s: uncorrectable ECC error\n", __func__);
|
||||
return -EBADMSG;
|
||||
}
|
||||
EXPORT_SYMBOL(__nand_correct_data);
|
||||
EXPORT_SYMBOL(ecc_sw_hamming_correct);
|
||||
|
||||
/**
|
||||
* nand_correct_data - [NAND Interface] Detect and correct bit error(s)
|
||||
* @chip: NAND chip object
|
||||
* @buf: raw data read from the chip
|
||||
* @read_ecc: ECC from the chip
|
||||
* @calc_ecc: the ECC calculated from raw data
|
||||
* nand_ecc_sw_hamming_correct - Detect and correct bit error(s)
|
||||
* @nand: NAND device
|
||||
* @buf: Raw data read from the chip
|
||||
* @read_ecc: ECC bytes read from the chip
|
||||
* @calc_ecc: ECC calculated from the raw data
|
||||
*
|
||||
* Detect and correct a 1 bit error for 256/512 byte block
|
||||
* Detect and correct up to 1 bit error per 256/512-byte block.
|
||||
*/
|
||||
int nand_correct_data(struct nand_chip *chip, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf,
|
||||
unsigned char *read_ecc,
|
||||
unsigned char *calc_ecc)
|
||||
{
|
||||
bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER;
|
||||
struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
unsigned int step_size = nand->ecc.ctx.conf.step_size;
|
||||
|
||||
return __nand_correct_data(buf, read_ecc, calc_ecc, chip->ecc.size,
|
||||
sm_order);
|
||||
return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc, step_size,
|
||||
engine_conf->sm_order);
|
||||
}
|
||||
EXPORT_SYMBOL(nand_correct_data);
|
||||
EXPORT_SYMBOL(nand_ecc_sw_hamming_correct);
|
||||
|
||||
int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand)
|
||||
{
|
||||
struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
|
||||
struct nand_ecc_sw_hamming_conf *engine_conf;
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
int ret;
|
||||
|
||||
if (!mtd->ooblayout) {
|
||||
switch (mtd->oobsize) {
|
||||
case 8:
|
||||
case 16:
|
||||
mtd_set_ooblayout(mtd, nand_get_small_page_ooblayout());
|
||||
break;
|
||||
case 64:
|
||||
case 128:
|
||||
mtd_set_ooblayout(mtd,
|
||||
nand_get_large_page_hamming_ooblayout());
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
conf->engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
|
||||
conf->algo = NAND_ECC_ALGO_HAMMING;
|
||||
conf->step_size = nand->ecc.user_conf.step_size;
|
||||
conf->strength = 1;
|
||||
|
||||
/* Use the strongest configuration by default */
|
||||
if (conf->step_size != 256 && conf->step_size != 512)
|
||||
conf->step_size = 256;
|
||||
|
||||
engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
|
||||
if (!engine_conf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = nand_ecc_init_req_tweaking(&engine_conf->req_ctx, nand);
|
||||
if (ret)
|
||||
goto free_engine_conf;
|
||||
|
||||
engine_conf->code_size = 3;
|
||||
engine_conf->nsteps = mtd->writesize / conf->step_size;
|
||||
engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
|
||||
engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
|
||||
if (!engine_conf->calc_buf || !engine_conf->code_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto free_bufs;
|
||||
}
|
||||
|
||||
nand->ecc.ctx.priv = engine_conf;
|
||||
nand->ecc.ctx.total = engine_conf->nsteps * engine_conf->code_size;
|
||||
|
||||
return 0;
|
||||
|
||||
free_bufs:
|
||||
nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
|
||||
kfree(engine_conf->calc_buf);
|
||||
kfree(engine_conf->code_buf);
|
||||
free_engine_conf:
|
||||
kfree(engine_conf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_sw_hamming_init_ctx);
|
||||
|
||||
void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand)
|
||||
{
|
||||
struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
|
||||
if (engine_conf) {
|
||||
nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
|
||||
kfree(engine_conf->calc_buf);
|
||||
kfree(engine_conf->code_buf);
|
||||
kfree(engine_conf);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_sw_hamming_cleanup_ctx);
|
||||
|
||||
static int nand_ecc_sw_hamming_prepare_io_req(struct nand_device *nand,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
int eccsize = nand->ecc.ctx.conf.step_size;
|
||||
int eccbytes = engine_conf->code_size;
|
||||
int eccsteps = engine_conf->nsteps;
|
||||
int total = nand->ecc.ctx.total;
|
||||
u8 *ecccalc = engine_conf->calc_buf;
|
||||
const u8 *data;
|
||||
int i;
|
||||
|
||||
/* Nothing to do for a raw operation */
|
||||
if (req->mode == MTD_OPS_RAW)
|
||||
return 0;
|
||||
|
||||
/* This engine does not provide BBM/free OOB bytes protection */
|
||||
if (!req->datalen)
|
||||
return 0;
|
||||
|
||||
nand_ecc_tweak_req(&engine_conf->req_ctx, req);
|
||||
|
||||
/* No more preparation for page read */
|
||||
if (req->type == NAND_PAGE_READ)
|
||||
return 0;
|
||||
|
||||
/* Preparation for page write: derive the ECC bytes and place them */
|
||||
for (i = 0, data = req->databuf.out;
|
||||
eccsteps;
|
||||
eccsteps--, i += eccbytes, data += eccsize)
|
||||
nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]);
|
||||
|
||||
return mtd_ooblayout_set_eccbytes(mtd, ecccalc, (void *)req->oobbuf.out,
|
||||
0, total);
|
||||
}
|
||||
|
||||
static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
int eccsize = nand->ecc.ctx.conf.step_size;
|
||||
int total = nand->ecc.ctx.total;
|
||||
int eccbytes = engine_conf->code_size;
|
||||
int eccsteps = engine_conf->nsteps;
|
||||
u8 *ecccalc = engine_conf->calc_buf;
|
||||
u8 *ecccode = engine_conf->code_buf;
|
||||
unsigned int max_bitflips = 0;
|
||||
u8 *data = req->databuf.in;
|
||||
int i, ret;
|
||||
|
||||
/* Nothing to do for a raw operation */
|
||||
if (req->mode == MTD_OPS_RAW)
|
||||
return 0;
|
||||
|
||||
/* This engine does not provide BBM/free OOB bytes protection */
|
||||
if (!req->datalen)
|
||||
return 0;
|
||||
|
||||
/* No more preparation for page write */
|
||||
if (req->type == NAND_PAGE_WRITE) {
|
||||
nand_ecc_restore_req(&engine_conf->req_ctx, req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Finish a page read: retrieve the (raw) ECC bytes*/
|
||||
ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, req->oobbuf.in, 0,
|
||||
total);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Calculate the ECC bytes */
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize)
|
||||
nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]);
|
||||
|
||||
/* Finish a page read: compare and correct */
|
||||
for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in;
|
||||
eccsteps;
|
||||
eccsteps--, i += eccbytes, data += eccsize) {
|
||||
int stat = nand_ecc_sw_hamming_correct(nand, data,
|
||||
&ecccode[i],
|
||||
&ecccalc[i]);
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
|
||||
nand_ecc_restore_req(&engine_conf->req_ctx, req);
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
static struct nand_ecc_engine_ops nand_ecc_sw_hamming_engine_ops = {
|
||||
.init_ctx = nand_ecc_sw_hamming_init_ctx,
|
||||
.cleanup_ctx = nand_ecc_sw_hamming_cleanup_ctx,
|
||||
.prepare_io_req = nand_ecc_sw_hamming_prepare_io_req,
|
||||
.finish_io_req = nand_ecc_sw_hamming_finish_io_req,
|
||||
};
|
||||
|
||||
static struct nand_ecc_engine nand_ecc_sw_hamming_engine = {
|
||||
.ops = &nand_ecc_sw_hamming_engine_ops,
|
||||
};
|
||||
|
||||
struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void)
|
||||
{
|
||||
return &nand_ecc_sw_hamming_engine;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_sw_hamming_get_engine);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Frans Meulenbroeks <fransmeulenbroeks@gmail.com>");
|
||||
MODULE_DESCRIPTION("Generic NAND ECC support");
|
||||
MODULE_DESCRIPTION("NAND software Hamming ECC support");
|
|
@ -95,6 +95,7 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/**
|
||||
* nand_ecc_init_ctx - Init the ECC engine context
|
||||
|
@ -104,7 +105,7 @@
|
|||
*/
|
||||
int nand_ecc_init_ctx(struct nand_device *nand)
|
||||
{
|
||||
if (!nand->ecc.engine->ops->init_ctx)
|
||||
if (!nand->ecc.engine || !nand->ecc.engine->ops->init_ctx)
|
||||
return 0;
|
||||
|
||||
return nand->ecc.engine->ops->init_ctx(nand);
|
||||
|
@ -117,7 +118,7 @@ EXPORT_SYMBOL(nand_ecc_init_ctx);
|
|||
*/
|
||||
void nand_ecc_cleanup_ctx(struct nand_device *nand)
|
||||
{
|
||||
if (nand->ecc.engine->ops->cleanup_ctx)
|
||||
if (nand->ecc.engine && nand->ecc.engine->ops->cleanup_ctx)
|
||||
nand->ecc.engine->ops->cleanup_ctx(nand);
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_cleanup_ctx);
|
||||
|
@ -130,7 +131,7 @@ EXPORT_SYMBOL(nand_ecc_cleanup_ctx);
|
|||
int nand_ecc_prepare_io_req(struct nand_device *nand,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
if (!nand->ecc.engine->ops->prepare_io_req)
|
||||
if (!nand->ecc.engine || !nand->ecc.engine->ops->prepare_io_req)
|
||||
return 0;
|
||||
|
||||
return nand->ecc.engine->ops->prepare_io_req(nand, req);
|
||||
|
@ -145,7 +146,7 @@ EXPORT_SYMBOL(nand_ecc_prepare_io_req);
|
|||
int nand_ecc_finish_io_req(struct nand_device *nand,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
if (!nand->ecc.engine->ops->finish_io_req)
|
||||
if (!nand->ecc.engine || !nand->ecc.engine->ops->finish_io_req)
|
||||
return 0;
|
||||
|
||||
return nand->ecc.engine->ops->finish_io_req(nand, req);
|
||||
|
@ -479,6 +480,137 @@ bool nand_ecc_is_strong_enough(struct nand_device *nand)
|
|||
}
|
||||
EXPORT_SYMBOL(nand_ecc_is_strong_enough);
|
||||
|
||||
/* ECC engine driver internal helpers */
|
||||
int nand_ecc_init_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx,
|
||||
struct nand_device *nand)
|
||||
{
|
||||
unsigned int total_buffer_size;
|
||||
|
||||
ctx->nand = nand;
|
||||
|
||||
/* Let the user decide the exact length of each buffer */
|
||||
if (!ctx->page_buffer_size)
|
||||
ctx->page_buffer_size = nanddev_page_size(nand);
|
||||
if (!ctx->oob_buffer_size)
|
||||
ctx->oob_buffer_size = nanddev_per_page_oobsize(nand);
|
||||
|
||||
total_buffer_size = ctx->page_buffer_size + ctx->oob_buffer_size;
|
||||
|
||||
ctx->spare_databuf = kzalloc(total_buffer_size, GFP_KERNEL);
|
||||
if (!ctx->spare_databuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->spare_oobbuf = ctx->spare_databuf + ctx->page_buffer_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_ecc_init_req_tweaking);
|
||||
|
||||
void nand_ecc_cleanup_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx)
|
||||
{
|
||||
kfree(ctx->spare_databuf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_ecc_cleanup_req_tweaking);
|
||||
|
||||
/*
|
||||
* Ensure data and OOB area is fully read/written otherwise the correction might
|
||||
* not work as expected.
|
||||
*/
|
||||
void nand_ecc_tweak_req(struct nand_ecc_req_tweak_ctx *ctx,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_device *nand = ctx->nand;
|
||||
struct nand_page_io_req *orig, *tweak;
|
||||
|
||||
/* Save the original request */
|
||||
ctx->orig_req = *req;
|
||||
ctx->bounce_data = false;
|
||||
ctx->bounce_oob = false;
|
||||
orig = &ctx->orig_req;
|
||||
tweak = req;
|
||||
|
||||
/* Ensure the request covers the entire page */
|
||||
if (orig->datalen < nanddev_page_size(nand)) {
|
||||
ctx->bounce_data = true;
|
||||
tweak->dataoffs = 0;
|
||||
tweak->datalen = nanddev_page_size(nand);
|
||||
tweak->databuf.in = ctx->spare_databuf;
|
||||
memset(tweak->databuf.in, 0xFF, ctx->page_buffer_size);
|
||||
}
|
||||
|
||||
if (orig->ooblen < nanddev_per_page_oobsize(nand)) {
|
||||
ctx->bounce_oob = true;
|
||||
tweak->ooboffs = 0;
|
||||
tweak->ooblen = nanddev_per_page_oobsize(nand);
|
||||
tweak->oobbuf.in = ctx->spare_oobbuf;
|
||||
memset(tweak->oobbuf.in, 0xFF, ctx->oob_buffer_size);
|
||||
}
|
||||
|
||||
/* Copy the data that must be writen in the bounce buffers, if needed */
|
||||
if (orig->type == NAND_PAGE_WRITE) {
|
||||
if (ctx->bounce_data)
|
||||
memcpy((void *)tweak->databuf.out + orig->dataoffs,
|
||||
orig->databuf.out, orig->datalen);
|
||||
|
||||
if (ctx->bounce_oob)
|
||||
memcpy((void *)tweak->oobbuf.out + orig->ooboffs,
|
||||
orig->oobbuf.out, orig->ooblen);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_ecc_tweak_req);
|
||||
|
||||
void nand_ecc_restore_req(struct nand_ecc_req_tweak_ctx *ctx,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_page_io_req *orig, *tweak;
|
||||
|
||||
orig = &ctx->orig_req;
|
||||
tweak = req;
|
||||
|
||||
/* Restore the data read from the bounce buffers, if needed */
|
||||
if (orig->type == NAND_PAGE_READ) {
|
||||
if (ctx->bounce_data)
|
||||
memcpy(orig->databuf.in,
|
||||
tweak->databuf.in + orig->dataoffs,
|
||||
orig->datalen);
|
||||
|
||||
if (ctx->bounce_oob)
|
||||
memcpy(orig->oobbuf.in,
|
||||
tweak->oobbuf.in + orig->ooboffs,
|
||||
orig->ooblen);
|
||||
}
|
||||
|
||||
/* Ensure the original request is restored */
|
||||
*req = *orig;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_ecc_restore_req);
|
||||
|
||||
struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand)
|
||||
{
|
||||
unsigned int algo = nand->ecc.user_conf.algo;
|
||||
|
||||
if (algo == NAND_ECC_ALGO_UNKNOWN)
|
||||
algo = nand->ecc.defaults.algo;
|
||||
|
||||
switch (algo) {
|
||||
case NAND_ECC_ALGO_HAMMING:
|
||||
return nand_ecc_sw_hamming_get_engine();
|
||||
case NAND_ECC_ALGO_BCH:
|
||||
return nand_ecc_sw_bch_get_engine();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_get_sw_engine);
|
||||
|
||||
struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand)
|
||||
{
|
||||
return nand->ecc.ondie_engine;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Generic ECC engine");
|
||||
|
|
|
@ -132,7 +132,7 @@ static const struct mtd_ooblayout_ops onenand_oob_128_ooblayout_ops = {
|
|||
.free = onenand_ooblayout_128_free,
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* onenand_oob_32_64 - oob info for large (2KB) page
|
||||
*/
|
||||
static int onenand_ooblayout_32_64_ecc(struct mtd_info *mtd, int section,
|
||||
|
@ -192,7 +192,7 @@ static const unsigned char ffchars[] = {
|
|||
|
||||
/**
|
||||
* onenand_readw - [OneNAND Interface] Read OneNAND register
|
||||
* @param addr address to read
|
||||
* @addr: address to read
|
||||
*
|
||||
* Read OneNAND register
|
||||
*/
|
||||
|
@ -203,8 +203,8 @@ static unsigned short onenand_readw(void __iomem *addr)
|
|||
|
||||
/**
|
||||
* onenand_writew - [OneNAND Interface] Write OneNAND register with value
|
||||
* @param value value to write
|
||||
* @param addr address to write
|
||||
* @value: value to write
|
||||
* @addr: address to write
|
||||
*
|
||||
* Write OneNAND register with value
|
||||
*/
|
||||
|
@ -215,8 +215,8 @@ static void onenand_writew(unsigned short value, void __iomem *addr)
|
|||
|
||||
/**
|
||||
* onenand_block_address - [DEFAULT] Get block address
|
||||
* @param this onenand chip data structure
|
||||
* @param block the block
|
||||
* @this: onenand chip data structure
|
||||
* @block: the block
|
||||
* @return translated block address if DDP, otherwise same
|
||||
*
|
||||
* Setup Start Address 1 Register (F100h)
|
||||
|
@ -232,8 +232,8 @@ static int onenand_block_address(struct onenand_chip *this, int block)
|
|||
|
||||
/**
|
||||
* onenand_bufferram_address - [DEFAULT] Get bufferram address
|
||||
* @param this onenand chip data structure
|
||||
* @param block the block
|
||||
* @this: onenand chip data structure
|
||||
* @block: the block
|
||||
* @return set DBS value if DDP, otherwise 0
|
||||
*
|
||||
* Setup Start Address 2 Register (F101h) for DDP
|
||||
|
@ -249,8 +249,8 @@ static int onenand_bufferram_address(struct onenand_chip *this, int block)
|
|||
|
||||
/**
|
||||
* onenand_page_address - [DEFAULT] Get page address
|
||||
* @param page the page address
|
||||
* @param sector the sector address
|
||||
* @page: the page address
|
||||
* @sector: the sector address
|
||||
* @return combined page and sector address
|
||||
*
|
||||
* Setup Start Address 8 Register (F107h)
|
||||
|
@ -268,10 +268,10 @@ static int onenand_page_address(int page, int sector)
|
|||
|
||||
/**
|
||||
* onenand_buffer_address - [DEFAULT] Get buffer address
|
||||
* @param dataram1 DataRAM index
|
||||
* @param sectors the sector address
|
||||
* @param count the number of sectors
|
||||
* @return the start buffer value
|
||||
* @dataram1: DataRAM index
|
||||
* @sectors: the sector address
|
||||
* @count: the number of sectors
|
||||
* Return: the start buffer value
|
||||
*
|
||||
* Setup Start Buffer Register (F200h)
|
||||
*/
|
||||
|
@ -295,8 +295,8 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
|
|||
|
||||
/**
|
||||
* flexonenand_block- For given address return block number
|
||||
* @param this - OneNAND device structure
|
||||
* @param addr - Address for which block number is needed
|
||||
* @this: - OneNAND device structure
|
||||
* @addr: - Address for which block number is needed
|
||||
*/
|
||||
static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr)
|
||||
{
|
||||
|
@ -359,7 +359,7 @@ EXPORT_SYMBOL(onenand_addr);
|
|||
|
||||
/**
|
||||
* onenand_get_density - [DEFAULT] Get OneNAND density
|
||||
* @param dev_id OneNAND device ID
|
||||
* @dev_id: OneNAND device ID
|
||||
*
|
||||
* Get OneNAND density from device ID
|
||||
*/
|
||||
|
@ -371,8 +371,8 @@ static inline int onenand_get_density(int dev_id)
|
|||
|
||||
/**
|
||||
* flexonenand_region - [Flex-OneNAND] Return erase region of addr
|
||||
* @param mtd MTD device structure
|
||||
* @param addr address whose erase region needs to be identified
|
||||
* @mtd: MTD device structure
|
||||
* @addr: address whose erase region needs to be identified
|
||||
*/
|
||||
int flexonenand_region(struct mtd_info *mtd, loff_t addr)
|
||||
{
|
||||
|
@ -387,10 +387,10 @@ EXPORT_SYMBOL(flexonenand_region);
|
|||
|
||||
/**
|
||||
* onenand_command - [DEFAULT] Send command to OneNAND device
|
||||
* @param mtd MTD device structure
|
||||
* @param cmd the command to be sent
|
||||
* @param addr offset to read from or write to
|
||||
* @param len number of bytes to read or write
|
||||
* @mtd: MTD device structure
|
||||
* @cmd: the command to be sent
|
||||
* @addr: offset to read from or write to
|
||||
* @len: number of bytes to read or write
|
||||
*
|
||||
* Send command to OneNAND device. This function is used for middle/large page
|
||||
* devices (1KB/2KB Bytes per page)
|
||||
|
@ -519,7 +519,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
|
|||
|
||||
/**
|
||||
* onenand_read_ecc - return ecc status
|
||||
* @param this onenand chip structure
|
||||
* @this: onenand chip structure
|
||||
*/
|
||||
static inline int onenand_read_ecc(struct onenand_chip *this)
|
||||
{
|
||||
|
@ -543,8 +543,8 @@ static inline int onenand_read_ecc(struct onenand_chip *this)
|
|||
|
||||
/**
|
||||
* onenand_wait - [DEFAULT] wait until the command is done
|
||||
* @param mtd MTD device structure
|
||||
* @param state state to select the max. timeout value
|
||||
* @mtd: MTD device structure
|
||||
* @state: state to select the max. timeout value
|
||||
*
|
||||
* Wait for command done. This applies to all OneNAND command
|
||||
* Read can take up to 30us, erase up to 2ms and program up to 350us
|
||||
|
@ -625,8 +625,8 @@ static int onenand_wait(struct mtd_info *mtd, int state)
|
|||
|
||||
/*
|
||||
* onenand_interrupt - [DEFAULT] onenand interrupt handler
|
||||
* @param irq onenand interrupt number
|
||||
* @param dev_id interrupt data
|
||||
* @irq: onenand interrupt number
|
||||
* @dev_id: interrupt data
|
||||
*
|
||||
* complete the work
|
||||
*/
|
||||
|
@ -643,8 +643,8 @@ static irqreturn_t onenand_interrupt(int irq, void *data)
|
|||
|
||||
/*
|
||||
* onenand_interrupt_wait - [DEFAULT] wait until the command is done
|
||||
* @param mtd MTD device structure
|
||||
* @param state state to select the max. timeout value
|
||||
* @mtd: MTD device structure
|
||||
* @state: state to select the max. timeout value
|
||||
*
|
||||
* Wait for command done.
|
||||
*/
|
||||
|
@ -659,8 +659,8 @@ static int onenand_interrupt_wait(struct mtd_info *mtd, int state)
|
|||
|
||||
/*
|
||||
* onenand_try_interrupt_wait - [DEFAULT] try interrupt wait
|
||||
* @param mtd MTD device structure
|
||||
* @param state state to select the max. timeout value
|
||||
* @mtd: MTD device structure
|
||||
* @state: state to select the max. timeout value
|
||||
*
|
||||
* Try interrupt based wait (It is used one-time)
|
||||
*/
|
||||
|
@ -689,7 +689,7 @@ static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state)
|
|||
|
||||
/*
|
||||
* onenand_setup_wait - [OneNAND Interface] setup onenand wait method
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*
|
||||
* There's two method to wait onenand work
|
||||
* 1. polling - read interrupt status register
|
||||
|
@ -724,8 +724,8 @@ static void onenand_setup_wait(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_bufferram_offset - [DEFAULT] BufferRAM offset
|
||||
* @param mtd MTD data structure
|
||||
* @param area BufferRAM area
|
||||
* @mtd: MTD data structure
|
||||
* @area: BufferRAM area
|
||||
* @return offset given area
|
||||
*
|
||||
* Return BufferRAM offset given area
|
||||
|
@ -747,11 +747,11 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
|
|||
|
||||
/**
|
||||
* onenand_read_bufferram - [OneNAND Interface] Read the bufferram area
|
||||
* @param mtd MTD data structure
|
||||
* @param area BufferRAM area
|
||||
* @param buffer the databuffer to put/get data
|
||||
* @param offset offset to read from or write to
|
||||
* @param count number of bytes to read/write
|
||||
* @mtd: MTD data structure
|
||||
* @area: BufferRAM area
|
||||
* @buffer: the databuffer to put/get data
|
||||
* @offset: offset to read from or write to
|
||||
* @count: number of bytes to read/write
|
||||
*
|
||||
* Read the BufferRAM area
|
||||
*/
|
||||
|
@ -783,11 +783,11 @@ static int onenand_read_bufferram(struct mtd_info *mtd, int area,
|
|||
|
||||
/**
|
||||
* onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
|
||||
* @param mtd MTD data structure
|
||||
* @param area BufferRAM area
|
||||
* @param buffer the databuffer to put/get data
|
||||
* @param offset offset to read from or write to
|
||||
* @param count number of bytes to read/write
|
||||
* @mtd: MTD data structure
|
||||
* @area: BufferRAM area
|
||||
* @buffer: the databuffer to put/get data
|
||||
* @offset: offset to read from or write to
|
||||
* @count: number of bytes to read/write
|
||||
*
|
||||
* Read the BufferRAM area with Sync. Burst Mode
|
||||
*/
|
||||
|
@ -823,11 +823,11 @@ static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area,
|
|||
|
||||
/**
|
||||
* onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
|
||||
* @param mtd MTD data structure
|
||||
* @param area BufferRAM area
|
||||
* @param buffer the databuffer to put/get data
|
||||
* @param offset offset to read from or write to
|
||||
* @param count number of bytes to read/write
|
||||
* @mtd: MTD data structure
|
||||
* @area: BufferRAM area
|
||||
* @buffer: the databuffer to put/get data
|
||||
* @offset: offset to read from or write to
|
||||
* @count: number of bytes to read/write
|
||||
*
|
||||
* Write the BufferRAM area
|
||||
*/
|
||||
|
@ -864,8 +864,8 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
|
|||
|
||||
/**
|
||||
* onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
|
||||
* @param mtd MTD data structure
|
||||
* @param addr address to check
|
||||
* @mtd: MTD data structure
|
||||
* @addr: address to check
|
||||
* @return blockpage address
|
||||
*
|
||||
* Get blockpage address at 2x program mode
|
||||
|
@ -888,8 +888,8 @@ static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
|
|||
|
||||
/**
|
||||
* onenand_check_bufferram - [GENERIC] Check BufferRAM information
|
||||
* @param mtd MTD data structure
|
||||
* @param addr address to check
|
||||
* @mtd: MTD data structure
|
||||
* @addr: address to check
|
||||
* @return 1 if there are valid data, otherwise 0
|
||||
*
|
||||
* Check bufferram if there is data we required
|
||||
|
@ -930,9 +930,9 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
|
|||
|
||||
/**
|
||||
* onenand_update_bufferram - [GENERIC] Update BufferRAM information
|
||||
* @param mtd MTD data structure
|
||||
* @param addr address to update
|
||||
* @param valid valid flag
|
||||
* @mtd: MTD data structure
|
||||
* @addr: address to update
|
||||
* @valid: valid flag
|
||||
*
|
||||
* Update BufferRAM information
|
||||
*/
|
||||
|
@ -963,9 +963,9 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
|
|||
|
||||
/**
|
||||
* onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
|
||||
* @param mtd MTD data structure
|
||||
* @param addr start address to invalidate
|
||||
* @param len length to invalidate
|
||||
* @mtd: MTD data structure
|
||||
* @addr: start address to invalidate
|
||||
* @len: length to invalidate
|
||||
*
|
||||
* Invalidate BufferRAM information
|
||||
*/
|
||||
|
@ -986,8 +986,8 @@ static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
|
|||
|
||||
/**
|
||||
* onenand_get_device - [GENERIC] Get chip for selected access
|
||||
* @param mtd MTD device structure
|
||||
* @param new_state the state which is requested
|
||||
* @mtd: MTD device structure
|
||||
* @new_state: the state which is requested
|
||||
*
|
||||
* Get the device and lock it for exclusive access
|
||||
*/
|
||||
|
@ -1024,7 +1024,7 @@ static int onenand_get_device(struct mtd_info *mtd, int new_state)
|
|||
|
||||
/**
|
||||
* onenand_release_device - [GENERIC] release chip
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*
|
||||
* Deselect, release chip lock and wake up anyone waiting on the device
|
||||
*/
|
||||
|
@ -1043,10 +1043,10 @@ static void onenand_release_device(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
|
||||
* @param mtd MTD device structure
|
||||
* @param buf destination address
|
||||
* @param column oob offset to read from
|
||||
* @param thislen oob length to read
|
||||
* @mtd: MTD device structure
|
||||
* @buf: destination address
|
||||
* @column: oob offset to read from
|
||||
* @thislen: oob length to read
|
||||
*/
|
||||
static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
|
||||
int thislen)
|
||||
|
@ -1061,9 +1061,9 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
|
|||
|
||||
/**
|
||||
* onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
|
||||
* @param mtd MTD device structure
|
||||
* @param addr address to recover
|
||||
* @param status return value from onenand_wait / onenand_bbt_wait
|
||||
* @mtd: MTD device structure
|
||||
* @addr: address to recover
|
||||
* @status: return value from onenand_wait / onenand_bbt_wait
|
||||
*
|
||||
* MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
|
||||
* lower page address and MSB page has higher page address in paired pages.
|
||||
|
@ -1104,9 +1104,9 @@ static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
|
|||
|
||||
/**
|
||||
* onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band
|
||||
* @param mtd MTD device structure
|
||||
* @param from offset to read from
|
||||
* @param ops: oob operation description structure
|
||||
* @mtd: MTD device structure
|
||||
* @from: offset to read from
|
||||
* @ops: oob operation description structure
|
||||
*
|
||||
* MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram.
|
||||
* So, read-while-load is not present.
|
||||
|
@ -1206,9 +1206,9 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
/**
|
||||
* onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
|
||||
* @param mtd MTD device structure
|
||||
* @param from offset to read from
|
||||
* @param ops: oob operation description structure
|
||||
* @mtd: MTD device structure
|
||||
* @from: offset to read from
|
||||
* @ops: oob operation description structure
|
||||
*
|
||||
* OneNAND read main and/or out-of-band data
|
||||
*/
|
||||
|
@ -1335,9 +1335,9 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
/**
|
||||
* onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
|
||||
* @param mtd MTD device structure
|
||||
* @param from offset to read from
|
||||
* @param ops: oob operation description structure
|
||||
* @mtd: MTD device structure
|
||||
* @from: offset to read from
|
||||
* @ops: oob operation description structure
|
||||
*
|
||||
* OneNAND read out-of-band data from the spare area
|
||||
*/
|
||||
|
@ -1430,10 +1430,10 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
/**
|
||||
* onenand_read_oob - [MTD Interface] Read main and/or out-of-band
|
||||
* @param mtd: MTD device structure
|
||||
* @param from: offset to read from
|
||||
* @param ops: oob operation description structure
|
||||
|
||||
* @mtd: MTD device structure
|
||||
* @from: offset to read from
|
||||
* @ops: oob operation description structure
|
||||
*
|
||||
* Read main and/or out-of-band
|
||||
*/
|
||||
static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
|
@ -1466,8 +1466,8 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
/**
|
||||
* onenand_bbt_wait - [DEFAULT] wait until the command is done
|
||||
* @param mtd MTD device structure
|
||||
* @param state state to select the max. timeout value
|
||||
* @mtd: MTD device structure
|
||||
* @state: state to select the max. timeout value
|
||||
*
|
||||
* Wait for command done.
|
||||
*/
|
||||
|
@ -1517,9 +1517,9 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)
|
|||
|
||||
/**
|
||||
* onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
|
||||
* @param mtd MTD device structure
|
||||
* @param from offset to read from
|
||||
* @param ops oob operation description structure
|
||||
* @mtd: MTD device structure
|
||||
* @from: offset to read from
|
||||
* @ops: oob operation description structure
|
||||
*
|
||||
* OneNAND read out-of-band data from the spare area for bbt scan
|
||||
*/
|
||||
|
@ -1594,9 +1594,9 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
|
||||
/**
|
||||
* onenand_verify_oob - [GENERIC] verify the oob contents after a write
|
||||
* @param mtd MTD device structure
|
||||
* @param buf the databuffer to verify
|
||||
* @param to offset to read from
|
||||
* @mtd: MTD device structure
|
||||
* @buf: the databuffer to verify
|
||||
* @to: offset to read from
|
||||
*/
|
||||
static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
|
||||
{
|
||||
|
@ -1622,10 +1622,10 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
|
|||
|
||||
/**
|
||||
* onenand_verify - [GENERIC] verify the chip contents after a write
|
||||
* @param mtd MTD device structure
|
||||
* @param buf the databuffer to verify
|
||||
* @param addr offset to read from
|
||||
* @param len number of bytes to read and compare
|
||||
* @mtd: MTD device structure
|
||||
* @buf: the databuffer to verify
|
||||
* @addr: offset to read from
|
||||
* @len: number of bytes to read and compare
|
||||
*/
|
||||
static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
|
||||
{
|
||||
|
@ -1684,11 +1684,11 @@ static void onenand_panic_wait(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_panic_write - [MTD Interface] write buffer to FLASH in a panic context
|
||||
* @param mtd MTD device structure
|
||||
* @param to offset to write to
|
||||
* @param len number of bytes to write
|
||||
* @param retlen pointer to variable to store the number of written bytes
|
||||
* @param buf the data to write
|
||||
* @mtd: MTD device structure
|
||||
* @to: offset to write to
|
||||
* @len: number of bytes to write
|
||||
* @retlen: pointer to variable to store the number of written bytes
|
||||
* @buf: the data to write
|
||||
*
|
||||
* Write with ECC
|
||||
*/
|
||||
|
@ -1762,11 +1762,11 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
|
||||
/**
|
||||
* onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
|
||||
* @param mtd MTD device structure
|
||||
* @param oob_buf oob buffer
|
||||
* @param buf source address
|
||||
* @param column oob offset to write to
|
||||
* @param thislen oob length to write
|
||||
* @mtd: MTD device structure
|
||||
* @oob_buf: oob buffer
|
||||
* @buf: source address
|
||||
* @column: oob offset to write to
|
||||
* @thislen: oob length to write
|
||||
*/
|
||||
static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
|
||||
const u_char *buf, int column, int thislen)
|
||||
|
@ -1776,9 +1776,9 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
|
|||
|
||||
/**
|
||||
* onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
|
||||
* @param mtd MTD device structure
|
||||
* @param to offset to write to
|
||||
* @param ops oob operation description structure
|
||||
* @mtd: MTD device structure
|
||||
* @to: offset to write to
|
||||
* @ops: oob operation description structure
|
||||
*
|
||||
* Write main and/or oob with ECC
|
||||
*/
|
||||
|
@ -1957,12 +1957,9 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
|
|||
|
||||
/**
|
||||
* onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
|
||||
* @param mtd MTD device structure
|
||||
* @param to offset to write to
|
||||
* @param len number of bytes to write
|
||||
* @param retlen pointer to variable to store the number of written bytes
|
||||
* @param buf the data to write
|
||||
* @param mode operation mode
|
||||
* @mtd: MTD device structure
|
||||
* @to: offset to write to
|
||||
* @ops: oob operation description structure
|
||||
*
|
||||
* OneNAND write out-of-band
|
||||
*/
|
||||
|
@ -2070,9 +2067,9 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
|
|||
|
||||
/**
|
||||
* onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band
|
||||
* @param mtd: MTD device structure
|
||||
* @param to: offset to write
|
||||
* @param ops: oob operation description structure
|
||||
* @mtd: MTD device structure
|
||||
* @to: offset to write
|
||||
* @ops: oob operation description structure
|
||||
*/
|
||||
static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops)
|
||||
|
@ -2101,9 +2098,9 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
|
||||
/**
|
||||
* onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
|
||||
* @param mtd MTD device structure
|
||||
* @param ofs offset from device start
|
||||
* @param allowbbt 1, if its allowed to access the bbt area
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset from device start
|
||||
* @allowbbt: 1, if its allowed to access the bbt area
|
||||
*
|
||||
* Check, if the block is bad. Either by reading the bad block table or
|
||||
* calling of the scan function.
|
||||
|
@ -2144,9 +2141,9 @@ static int onenand_multiblock_erase_verify(struct mtd_info *mtd,
|
|||
|
||||
/**
|
||||
* onenand_multiblock_erase - [INTERN] erase block(s) using multiblock erase
|
||||
* @param mtd MTD device structure
|
||||
* @param instr erase instruction
|
||||
* @param region erase region
|
||||
* @mtd: MTD device structure
|
||||
* @instr: erase instruction
|
||||
* @block_size: block size
|
||||
*
|
||||
* Erase one or more blocks up to 64 block at a time
|
||||
*/
|
||||
|
@ -2254,10 +2251,10 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
|
|||
|
||||
/**
|
||||
* onenand_block_by_block_erase - [INTERN] erase block(s) using regular erase
|
||||
* @param mtd MTD device structure
|
||||
* @param instr erase instruction
|
||||
* @param region erase region
|
||||
* @param block_size erase block size
|
||||
* @mtd: MTD device structure
|
||||
* @instr: erase instruction
|
||||
* @region: erase region
|
||||
* @block_size: erase block size
|
||||
*
|
||||
* Erase one or more blocks one block at a time
|
||||
*/
|
||||
|
@ -2326,8 +2323,8 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd,
|
|||
|
||||
/**
|
||||
* onenand_erase - [MTD Interface] erase block(s)
|
||||
* @param mtd MTD device structure
|
||||
* @param instr erase instruction
|
||||
* @mtd: MTD device structure
|
||||
* @instr: erase instruction
|
||||
*
|
||||
* Erase one or more blocks
|
||||
*/
|
||||
|
@ -2391,7 +2388,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|||
|
||||
/**
|
||||
* onenand_sync - [MTD Interface] sync
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*
|
||||
* Sync is actually a wait for chip ready function
|
||||
*/
|
||||
|
@ -2408,8 +2405,8 @@ static void onenand_sync(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
|
||||
* @param mtd MTD device structure
|
||||
* @param ofs offset relative to mtd start
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset relative to mtd start
|
||||
*
|
||||
* Check whether the block is bad
|
||||
*/
|
||||
|
@ -2425,8 +2422,8 @@ static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
|||
|
||||
/**
|
||||
* onenand_default_block_markbad - [DEFAULT] mark a block bad
|
||||
* @param mtd MTD device structure
|
||||
* @param ofs offset from device start
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset from device start
|
||||
*
|
||||
* This is the default implementation, which can be overridden by
|
||||
* a hardware specific driver.
|
||||
|
@ -2460,8 +2457,8 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|||
|
||||
/**
|
||||
* onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
|
||||
* @param mtd MTD device structure
|
||||
* @param ofs offset relative to mtd start
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset relative to mtd start
|
||||
*
|
||||
* Mark the block as bad
|
||||
*/
|
||||
|
@ -2486,10 +2483,10 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|||
|
||||
/**
|
||||
* onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
|
||||
* @param mtd MTD device structure
|
||||
* @param ofs offset relative to mtd start
|
||||
* @param len number of bytes to lock or unlock
|
||||
* @param cmd lock or unlock command
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset relative to mtd start
|
||||
* @len: number of bytes to lock or unlock
|
||||
* @cmd: lock or unlock command
|
||||
*
|
||||
* Lock or unlock one or more blocks
|
||||
*/
|
||||
|
@ -2566,9 +2563,9 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
|
|||
|
||||
/**
|
||||
* onenand_lock - [MTD Interface] Lock block(s)
|
||||
* @param mtd MTD device structure
|
||||
* @param ofs offset relative to mtd start
|
||||
* @param len number of bytes to unlock
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset relative to mtd start
|
||||
* @len: number of bytes to unlock
|
||||
*
|
||||
* Lock one or more blocks
|
||||
*/
|
||||
|
@ -2584,9 +2581,9 @@ static int onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
|||
|
||||
/**
|
||||
* onenand_unlock - [MTD Interface] Unlock block(s)
|
||||
* @param mtd MTD device structure
|
||||
* @param ofs offset relative to mtd start
|
||||
* @param len number of bytes to unlock
|
||||
* @mtd: MTD device structure
|
||||
* @ofs: offset relative to mtd start
|
||||
* @len: number of bytes to unlock
|
||||
*
|
||||
* Unlock one or more blocks
|
||||
*/
|
||||
|
@ -2602,7 +2599,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
|||
|
||||
/**
|
||||
* onenand_check_lock_status - [OneNAND Interface] Check lock status
|
||||
* @param this onenand chip data structure
|
||||
* @this: onenand chip data structure
|
||||
*
|
||||
* Check lock status
|
||||
*/
|
||||
|
@ -2636,7 +2633,7 @@ static int onenand_check_lock_status(struct onenand_chip *this)
|
|||
|
||||
/**
|
||||
* onenand_unlock_all - [OneNAND Interface] unlock all blocks
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*
|
||||
* Unlock all blocks
|
||||
*/
|
||||
|
@ -2683,10 +2680,10 @@ static void onenand_unlock_all(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_otp_command - Send OTP specific command to OneNAND device
|
||||
* @param mtd MTD device structure
|
||||
* @param cmd the command to be sent
|
||||
* @param addr offset to read from or write to
|
||||
* @param len number of bytes to read or write
|
||||
* @mtd: MTD device structure
|
||||
* @cmd: the command to be sent
|
||||
* @addr: offset to read from or write to
|
||||
* @len: number of bytes to read or write
|
||||
*/
|
||||
static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
|
||||
size_t len)
|
||||
|
@ -2758,11 +2755,9 @@ static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
|
|||
|
||||
/**
|
||||
* onenand_otp_write_oob_nolock - [INTERN] OneNAND write out-of-band, specific to OTP
|
||||
* @param mtd MTD device structure
|
||||
* @param to offset to write to
|
||||
* @param len number of bytes to write
|
||||
* @param retlen pointer to variable to store the number of written bytes
|
||||
* @param buf the data to write
|
||||
* @mtd: MTD device structure
|
||||
* @to: offset to write to
|
||||
* @ops: oob operation description structure
|
||||
*
|
||||
* OneNAND write out-of-band only for OTP
|
||||
*/
|
||||
|
@ -2889,11 +2884,11 @@ typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
|
|||
|
||||
/**
|
||||
* do_otp_read - [DEFAULT] Read OTP block area
|
||||
* @param mtd MTD device structure
|
||||
* @param from The offset to read
|
||||
* @param len number of bytes to read
|
||||
* @param retlen pointer to variable to store the number of readbytes
|
||||
* @param buf the databuffer to put/get data
|
||||
* @mtd: MTD device structure
|
||||
* @from: The offset to read
|
||||
* @len: number of bytes to read
|
||||
* @retlen: pointer to variable to store the number of readbytes
|
||||
* @buf: the databuffer to put/get data
|
||||
*
|
||||
* Read OTP block area.
|
||||
*/
|
||||
|
@ -2926,11 +2921,11 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
|
||||
/**
|
||||
* do_otp_write - [DEFAULT] Write OTP block area
|
||||
* @param mtd MTD device structure
|
||||
* @param to The offset to write
|
||||
* @param len number of bytes to write
|
||||
* @param retlen pointer to variable to store the number of write bytes
|
||||
* @param buf the databuffer to put/get data
|
||||
* @mtd: MTD device structure
|
||||
* @to: The offset to write
|
||||
* @len: number of bytes to write
|
||||
* @retlen: pointer to variable to store the number of write bytes
|
||||
* @buf: the databuffer to put/get data
|
||||
*
|
||||
* Write OTP block area.
|
||||
*/
|
||||
|
@ -2970,11 +2965,11 @@ static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
|
||||
/**
|
||||
* do_otp_lock - [DEFAULT] Lock OTP block area
|
||||
* @param mtd MTD device structure
|
||||
* @param from The offset to lock
|
||||
* @param len number of bytes to lock
|
||||
* @param retlen pointer to variable to store the number of lock bytes
|
||||
* @param buf the databuffer to put/get data
|
||||
* @mtd: MTD device structure
|
||||
* @from: The offset to lock
|
||||
* @len: number of bytes to lock
|
||||
* @retlen: pointer to variable to store the number of lock bytes
|
||||
* @buf: the databuffer to put/get data
|
||||
*
|
||||
* Lock OTP block area.
|
||||
*/
|
||||
|
@ -3018,13 +3013,13 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
|
||||
/**
|
||||
* onenand_otp_walk - [DEFAULT] Handle OTP operation
|
||||
* @param mtd MTD device structure
|
||||
* @param from The offset to read/write
|
||||
* @param len number of bytes to read/write
|
||||
* @param retlen pointer to variable to store the number of read bytes
|
||||
* @param buf the databuffer to put/get data
|
||||
* @param action do given action
|
||||
* @param mode specify user and factory
|
||||
* @mtd: MTD device structure
|
||||
* @from: The offset to read/write
|
||||
* @len: number of bytes to read/write
|
||||
* @retlen: pointer to variable to store the number of read bytes
|
||||
* @buf: the databuffer to put/get data
|
||||
* @action: do given action
|
||||
* @mode: specify user and factory
|
||||
*
|
||||
* Handle OTP operation.
|
||||
*/
|
||||
|
@ -3099,10 +3094,10 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
|
||||
/**
|
||||
* onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
|
||||
* @param mtd MTD device structure
|
||||
* @param len number of bytes to read
|
||||
* @param retlen pointer to variable to store the number of read bytes
|
||||
* @param buf the databuffer to put/get data
|
||||
* @mtd: MTD device structure
|
||||
* @len: number of bytes to read
|
||||
* @retlen: pointer to variable to store the number of read bytes
|
||||
* @buf: the databuffer to put/get data
|
||||
*
|
||||
* Read factory OTP info.
|
||||
*/
|
||||
|
@ -3115,11 +3110,11 @@ static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len,
|
|||
|
||||
/**
|
||||
* onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area
|
||||
* @param mtd MTD device structure
|
||||
* @param from The offset to read
|
||||
* @param len number of bytes to read
|
||||
* @param retlen pointer to variable to store the number of read bytes
|
||||
* @param buf the databuffer to put/get data
|
||||
* @mtd: MTD device structure
|
||||
* @from: The offset to read
|
||||
* @len: number of bytes to read
|
||||
* @retlen: pointer to variable to store the number of read bytes
|
||||
* @buf: the databuffer to put/get data
|
||||
*
|
||||
* Read factory OTP area.
|
||||
*/
|
||||
|
@ -3131,10 +3126,10 @@ static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
/**
|
||||
* onenand_get_user_prot_info - [MTD Interface] Read user OTP info
|
||||
* @param mtd MTD device structure
|
||||
* @param retlen pointer to variable to store the number of read bytes
|
||||
* @param len number of bytes to read
|
||||
* @param buf the databuffer to put/get data
|
||||
* @mtd: MTD device structure
|
||||
* @retlen: pointer to variable to store the number of read bytes
|
||||
* @len: number of bytes to read
|
||||
* @buf: the databuffer to put/get data
|
||||
*
|
||||
* Read user OTP info.
|
||||
*/
|
||||
|
@ -3147,11 +3142,11 @@ static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len,
|
|||
|
||||
/**
|
||||
* onenand_read_user_prot_reg - [MTD Interface] Read user OTP area
|
||||
* @param mtd MTD device structure
|
||||
* @param from The offset to read
|
||||
* @param len number of bytes to read
|
||||
* @param retlen pointer to variable to store the number of read bytes
|
||||
* @param buf the databuffer to put/get data
|
||||
* @mtd: MTD device structure
|
||||
* @from: The offset to read
|
||||
* @len: number of bytes to read
|
||||
* @retlen: pointer to variable to store the number of read bytes
|
||||
* @buf: the databuffer to put/get data
|
||||
*
|
||||
* Read user OTP area.
|
||||
*/
|
||||
|
@ -3163,11 +3158,11 @@ static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
/**
|
||||
* onenand_write_user_prot_reg - [MTD Interface] Write user OTP area
|
||||
* @param mtd MTD device structure
|
||||
* @param from The offset to write
|
||||
* @param len number of bytes to write
|
||||
* @param retlen pointer to variable to store the number of write bytes
|
||||
* @param buf the databuffer to put/get data
|
||||
* @mtd: MTD device structure
|
||||
* @from: The offset to write
|
||||
* @len: number of bytes to write
|
||||
* @retlen: pointer to variable to store the number of write bytes
|
||||
* @buf: the databuffer to put/get data
|
||||
*
|
||||
* Write user OTP area.
|
||||
*/
|
||||
|
@ -3179,9 +3174,9 @@ static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
/**
|
||||
* onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area
|
||||
* @param mtd MTD device structure
|
||||
* @param from The offset to lock
|
||||
* @param len number of bytes to unlock
|
||||
* @mtd: MTD device structure
|
||||
* @from: The offset to lock
|
||||
* @len: number of bytes to unlock
|
||||
*
|
||||
* Write lock mark on spare area in page 0 in OTP block
|
||||
*/
|
||||
|
@ -3234,7 +3229,7 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
/**
|
||||
* onenand_check_features - Check and set OneNAND features
|
||||
* @param mtd MTD data structure
|
||||
* @mtd: MTD data structure
|
||||
*
|
||||
* Check and set OneNAND features
|
||||
* - lock scheme
|
||||
|
@ -3324,8 +3319,8 @@ static void onenand_check_features(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_print_device_info - Print device & version ID
|
||||
* @param device device ID
|
||||
* @param version version ID
|
||||
* @device: device ID
|
||||
* @version: version ID
|
||||
*
|
||||
* Print device & version ID
|
||||
*/
|
||||
|
@ -3355,7 +3350,7 @@ static const struct onenand_manufacturers onenand_manuf_ids[] = {
|
|||
|
||||
/**
|
||||
* onenand_check_maf - Check manufacturer ID
|
||||
* @param manuf manufacturer ID
|
||||
* @manuf: manufacturer ID
|
||||
*
|
||||
* Check manufacturer ID
|
||||
*/
|
||||
|
@ -3380,9 +3375,9 @@ static int onenand_check_maf(int manuf)
|
|||
}
|
||||
|
||||
/**
|
||||
* flexonenand_get_boundary - Reads the SLC boundary
|
||||
* @param onenand_info - onenand info structure
|
||||
**/
|
||||
* flexonenand_get_boundary - Reads the SLC boundary
|
||||
* @mtd: MTD data structure
|
||||
*/
|
||||
static int flexonenand_get_boundary(struct mtd_info *mtd)
|
||||
{
|
||||
struct onenand_chip *this = mtd->priv;
|
||||
|
@ -3422,7 +3417,7 @@ static int flexonenand_get_boundary(struct mtd_info *mtd)
|
|||
/**
|
||||
* flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
|
||||
* boundary[], diesize[], mtd->size, mtd->erasesize
|
||||
* @param mtd - MTD device structure
|
||||
* @mtd: - MTD device structure
|
||||
*/
|
||||
static void flexonenand_get_size(struct mtd_info *mtd)
|
||||
{
|
||||
|
@ -3493,9 +3488,9 @@ static void flexonenand_get_size(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* flexonenand_check_blocks_erased - Check if blocks are erased
|
||||
* @param mtd_info - mtd info structure
|
||||
* @param start - first erase block to check
|
||||
* @param end - last erase block to check
|
||||
* @mtd: mtd info structure
|
||||
* @start: first erase block to check
|
||||
* @end: last erase block to check
|
||||
*
|
||||
* Converting an unerased block from MLC to SLC
|
||||
* causes byte values to change. Since both data and its ECC
|
||||
|
@ -3548,9 +3543,8 @@ static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* flexonenand_set_boundary - Writes the SLC boundary
|
||||
* @param mtd - mtd info structure
|
||||
*/
|
||||
static int flexonenand_set_boundary(struct mtd_info *mtd, int die,
|
||||
int boundary, int lock)
|
||||
|
@ -3640,7 +3634,7 @@ static int flexonenand_set_boundary(struct mtd_info *mtd, int die,
|
|||
|
||||
/**
|
||||
* onenand_chip_probe - [OneNAND Interface] The generic chip probe
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*
|
||||
* OneNAND detection method:
|
||||
* Compare the values from command with ones from register
|
||||
|
@ -3688,7 +3682,7 @@ static int onenand_chip_probe(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_probe - [OneNAND Interface] Probe the OneNAND device
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
static int onenand_probe(struct mtd_info *mtd)
|
||||
{
|
||||
|
@ -3783,7 +3777,7 @@ static int onenand_probe(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_suspend - [MTD Interface] Suspend the OneNAND flash
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
static int onenand_suspend(struct mtd_info *mtd)
|
||||
{
|
||||
|
@ -3792,7 +3786,7 @@ static int onenand_suspend(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_resume - [MTD Interface] Resume the OneNAND flash
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
static void onenand_resume(struct mtd_info *mtd)
|
||||
{
|
||||
|
@ -3807,8 +3801,8 @@ static void onenand_resume(struct mtd_info *mtd)
|
|||
|
||||
/**
|
||||
* onenand_scan - [OneNAND Interface] Scan for the OneNAND device
|
||||
* @param mtd MTD device structure
|
||||
* @param maxchips Number of chips to scan for
|
||||
* @mtd: MTD device structure
|
||||
* @maxchips: Number of chips to scan for
|
||||
*
|
||||
* This fills out all the not initialized function pointers
|
||||
* with the defaults.
|
||||
|
@ -3985,7 +3979,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
|||
|
||||
/**
|
||||
* onenand_release - [OneNAND Interface] Free resources held by the OneNAND device
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
void onenand_release(struct mtd_info *mtd)
|
||||
{
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
|
||||
/**
|
||||
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
|
||||
* @param buf the buffer to search
|
||||
* @param len the length of buffer to search
|
||||
* @param paglen the pagelength
|
||||
* @param td search pattern descriptor
|
||||
* @buf: the buffer to search
|
||||
* @len: the length of buffer to search
|
||||
* @paglen: the pagelength
|
||||
* @td: search pattern descriptor
|
||||
*
|
||||
* Check for a pattern at the given place. Used to search bad block
|
||||
* tables and good / bad block identifiers. Same as check_pattern, but
|
||||
|
@ -44,10 +44,10 @@ static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bb
|
|||
|
||||
/**
|
||||
* create_bbt - [GENERIC] Create a bad block table by scanning the device
|
||||
* @param mtd MTD device structure
|
||||
* @param buf temporary buffer
|
||||
* @param bd descriptor for the good/bad block search pattern
|
||||
* @param chip create the table for a specific chip, -1 read all chips.
|
||||
* @mtd: MTD device structure
|
||||
* @buf: temporary buffer
|
||||
* @bd: descriptor for the good/bad block search pattern
|
||||
* @chip: create the table for a specific chip, -1 read all chips.
|
||||
* Applies only if NAND_BBT_PERCHIP option is set
|
||||
*
|
||||
* Create a bad block table by scanning the device
|
||||
|
@ -122,8 +122,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
|||
|
||||
/**
|
||||
* onenand_memory_bbt - [GENERIC] create a memory based bad block table
|
||||
* @param mtd MTD device structure
|
||||
* @param bd descriptor for the good/bad block search pattern
|
||||
* @mtd: MTD device structure
|
||||
* @bd: descriptor for the good/bad block search pattern
|
||||
*
|
||||
* The function creates a memory based bbt by scanning the device
|
||||
* for manufacturer / software marked good / bad blocks
|
||||
|
@ -137,9 +137,9 @@ static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_desc
|
|||
|
||||
/**
|
||||
* onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad
|
||||
* @param mtd MTD device structure
|
||||
* @param offs offset in the device
|
||||
* @param allowbbt allow access to bad block table region
|
||||
* @mtd: MTD device structure
|
||||
* @offs: offset in the device
|
||||
* @allowbbt: allow access to bad block table region
|
||||
*/
|
||||
static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
|
||||
{
|
||||
|
@ -166,8 +166,8 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
|
|||
|
||||
/**
|
||||
* onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s)
|
||||
* @param mtd MTD device structure
|
||||
* @param bd descriptor for the good/bad block search pattern
|
||||
* @mtd: MTD device structure
|
||||
* @bd: descriptor for the good/bad block search pattern
|
||||
*
|
||||
* The function checks, if a bad block table(s) is/are already
|
||||
* available. If not it scans the device for manufacturer
|
||||
|
@ -221,7 +221,7 @@ static struct nand_bbt_descr largepage_memorybased = {
|
|||
|
||||
/**
|
||||
* onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device
|
||||
* @param mtd MTD device structure
|
||||
* @mtd: MTD device structure
|
||||
*
|
||||
* This function selects the default bad block table
|
||||
* support for the device and calls the onenand_scan_bbt function
|
||||
|
|
|
@ -371,12 +371,12 @@ static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area,
|
|||
|
||||
bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
|
||||
/*
|
||||
* If the buffer address is not DMA-able, len is not long enough to make
|
||||
* DMA transfers profitable or panic_write() may be in an interrupt
|
||||
* context fallback to PIO mode.
|
||||
* If the buffer address is not DMA-able, len is not long enough to
|
||||
* make DMA transfers profitable or if invoked from panic_write()
|
||||
* fallback to PIO mode.
|
||||
*/
|
||||
if (!virt_addr_valid(buf) || bram_offset & 3 || (size_t)buf & 3 ||
|
||||
count < 384 || in_interrupt() || oops_in_progress)
|
||||
count < 384 || mtd->oops_panic_write)
|
||||
goto out_copy;
|
||||
|
||||
xtra = count & 3;
|
||||
|
@ -418,12 +418,12 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area,
|
|||
|
||||
bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
|
||||
/*
|
||||
* If the buffer address is not DMA-able, len is not long enough to make
|
||||
* DMA transfers profitable or panic_write() may be in an interrupt
|
||||
* context fallback to PIO mode.
|
||||
* If the buffer address is not DMA-able, len is not long enough to
|
||||
* make DMA transfers profitable or if invoked from panic_write()
|
||||
* fallback to PIO mode.
|
||||
*/
|
||||
if (!virt_addr_valid(buf) || bram_offset & 3 || (size_t)buf & 3 ||
|
||||
count < 384 || in_interrupt() || oops_in_progress)
|
||||
count < 384 || mtd->oops_panic_write)
|
||||
goto out_copy;
|
||||
|
||||
dma_src = dma_map_single(dev, buf, count, DMA_TO_DEVICE);
|
||||
|
|
|
@ -1,20 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config MTD_NAND_ECC_SW_HAMMING
|
||||
tristate
|
||||
|
||||
config MTD_NAND_ECC_SW_HAMMING_SMC
|
||||
bool "NAND ECC Smart Media byte order"
|
||||
depends on MTD_NAND_ECC_SW_HAMMING
|
||||
default n
|
||||
help
|
||||
Software ECC according to the Smart Media Specification.
|
||||
The original Linux implementation had byte 0 and 1 swapped.
|
||||
|
||||
menuconfig MTD_RAW_NAND
|
||||
tristate "Raw/Parallel NAND Device Support"
|
||||
select MTD_NAND_CORE
|
||||
select MTD_NAND_ECC
|
||||
select MTD_NAND_ECC_SW_HAMMING
|
||||
help
|
||||
This enables support for accessing all type of raw/parallel
|
||||
NAND flash devices. For further information see
|
||||
|
@ -22,16 +10,6 @@ menuconfig MTD_RAW_NAND
|
|||
|
||||
if MTD_RAW_NAND
|
||||
|
||||
config MTD_NAND_ECC_SW_BCH
|
||||
bool "Support software BCH ECC"
|
||||
select BCH
|
||||
default n
|
||||
help
|
||||
This enables support for software BCH error correction. Binary BCH
|
||||
codes are more powerful and cpu intensive than traditional Hamming
|
||||
ECC codes. They are used with NAND devices requiring more than 1 bit
|
||||
of error correction.
|
||||
|
||||
comment "Raw/parallel NAND flash controllers"
|
||||
|
||||
config MTD_NAND_DENALI
|
||||
|
@ -93,6 +71,7 @@ config MTD_NAND_AU1550
|
|||
config MTD_NAND_NDFC
|
||||
tristate "IBM/MCC 4xx NAND controller"
|
||||
depends on 4xx
|
||||
select MTD_NAND_ECC_SW_HAMMING
|
||||
select MTD_NAND_ECC_SW_HAMMING_SMC
|
||||
help
|
||||
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
||||
|
@ -313,7 +292,7 @@ config MTD_NAND_VF610_NFC
|
|||
config MTD_NAND_MXC
|
||||
tristate "Freescale MXC NAND controller"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
depends on HAS_IOMEM && OF
|
||||
help
|
||||
This enables the driver for the NAND flash controller on the
|
||||
MXC processors.
|
||||
|
@ -462,6 +441,26 @@ config MTD_NAND_ARASAN
|
|||
Enables the driver for the Arasan NAND flash controller on
|
||||
Zynq Ultrascale+ MPSoC.
|
||||
|
||||
config MTD_NAND_INTEL_LGM
|
||||
tristate "Support for NAND controller on Intel LGM SoC"
|
||||
depends on OF || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash chips on Intel's LGM SoC.
|
||||
NAND flash controller interfaced through the External Bus Unit.
|
||||
|
||||
config MTD_NAND_ROCKCHIP
|
||||
tristate "Rockchip NAND controller"
|
||||
depends on ARCH_ROCKCHIP && HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND controller on Rockchip SoCs.
|
||||
There are four different versions of NAND FLASH Controllers,
|
||||
including:
|
||||
NFC v600: RK2928, RK3066, RK3188
|
||||
NFC v622: RK3036, RK3128
|
||||
NFC v800: RK3308, RV1108
|
||||
NFC v900: PX30, RK3326
|
||||
|
||||
comment "Misc"
|
||||
|
||||
config MTD_SM_COMMON
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_MTD_RAW_NAND) += nand.o
|
||||
obj-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += nand_ecc.o
|
||||
nand-$(CONFIG_MTD_NAND_ECC_SW_BCH) += nand_bch.o
|
||||
obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
|
||||
|
||||
obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
|
||||
|
@ -58,6 +56,8 @@ obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o
|
|||
obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_CADENCE) += cadence-nand-controller.o
|
||||
obj-$(CONFIG_MTD_NAND_ARASAN) += arasan-nand-controller.o
|
||||
obj-$(CONFIG_MTD_NAND_INTEL_LGM) += intel-nand-controller.o
|
||||
obj-$(CONFIG_MTD_NAND_ROCKCHIP) += rockchip-nand-controller.o
|
||||
|
||||
nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
|
||||
nand-objs += nand_onfi.o
|
||||
|
|
|
@ -118,6 +118,7 @@
|
|||
* @rdy_timeout_ms: Timeout for waits on Ready/Busy pin
|
||||
* @len: Data transfer length
|
||||
* @read: Data transfer direction from the controller point of view
|
||||
* @buf: Data buffer
|
||||
*/
|
||||
struct anfc_op {
|
||||
u32 pkt_reg;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright (C) 2004 Embedded Edge, LLC
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
|
|
@ -1846,7 +1846,7 @@ static void brcmnand_write_buf(struct nand_chip *chip, const uint8_t *buf,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Kick EDU engine
|
||||
*/
|
||||
static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
|
||||
|
@ -1937,7 +1937,7 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Construct a FLASH_DMA descriptor as part of a linked list. You must know the
|
||||
* following ahead of time:
|
||||
* - Is this descriptor the beginning or end of a linked list?
|
||||
|
@ -1970,7 +1970,7 @@ static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Kick the FLASH_DMA engine, with a given DMA descriptor
|
||||
*/
|
||||
static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
|
||||
|
|
|
@ -359,10 +359,10 @@ static int cafe_nand_read_oob(struct nand_chip *chip, int page)
|
|||
}
|
||||
/**
|
||||
* cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller expects OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* The hw generator calculates the error syndrome automatically. Therefore
|
||||
* we need a special oob layout and handling.
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
|
@ -252,7 +251,7 @@ static int cs553x_attach_chip(struct nand_chip *chip)
|
|||
chip->ecc.bytes = 3;
|
||||
chip->ecc.hwctl = cs_enable_hwecc;
|
||||
chip->ecc.calculate = cs_calculate_ecc;
|
||||
chip->ecc.correct = nand_correct_data;
|
||||
chip->ecc.correct = rawnand_sw_hamming_correct;
|
||||
chip->ecc.strength = 1;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -586,10 +586,10 @@ static int davinci_nand_attach_chip(struct nand_chip *chip)
|
|||
return PTR_ERR(pdata);
|
||||
|
||||
/* Use board-specific ECC config */
|
||||
info->chip.ecc.engine_type = pdata->engine_type;
|
||||
info->chip.ecc.placement = pdata->ecc_placement;
|
||||
chip->ecc.engine_type = pdata->engine_type;
|
||||
chip->ecc.placement = pdata->ecc_placement;
|
||||
|
||||
switch (info->chip.ecc.engine_type) {
|
||||
switch (chip->ecc.engine_type) {
|
||||
case NAND_ECC_ENGINE_TYPE_NONE:
|
||||
pdata->ecc_bits = 0;
|
||||
break;
|
||||
|
@ -601,7 +601,7 @@ static int davinci_nand_attach_chip(struct nand_chip *chip)
|
|||
* NAND_ECC_ALGO_HAMMING to avoid adding an extra ->ecc_algo
|
||||
* field to davinci_nand_pdata.
|
||||
*/
|
||||
info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
|
||||
chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
|
||||
break;
|
||||
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
||||
if (pdata->ecc_bits == 4) {
|
||||
|
@ -628,12 +628,12 @@ static int davinci_nand_attach_chip(struct nand_chip *chip)
|
|||
if (ret == -EBUSY)
|
||||
return ret;
|
||||
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_4bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_4bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
|
||||
info->chip.ecc.bytes = 10;
|
||||
info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
|
||||
info->chip.ecc.algo = NAND_ECC_ALGO_BCH;
|
||||
chip->ecc.calculate = nand_davinci_calculate_4bit;
|
||||
chip->ecc.correct = nand_davinci_correct_4bit;
|
||||
chip->ecc.hwctl = nand_davinci_hwctl_4bit;
|
||||
chip->ecc.bytes = 10;
|
||||
chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
|
||||
chip->ecc.algo = NAND_ECC_ALGO_BCH;
|
||||
|
||||
/*
|
||||
* Update ECC layout if needed ... for 1-bit HW ECC, the
|
||||
|
@ -651,20 +651,20 @@ static int davinci_nand_attach_chip(struct nand_chip *chip)
|
|||
} else if (chunks == 4 || chunks == 8) {
|
||||
mtd_set_ooblayout(mtd,
|
||||
nand_get_large_page_ooblayout());
|
||||
info->chip.ecc.read_page = nand_davinci_read_page_hwecc_oob_first;
|
||||
chip->ecc.read_page = nand_davinci_read_page_hwecc_oob_first;
|
||||
} else {
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
/* 1bit ecc hamming */
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_1bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
|
||||
info->chip.ecc.bytes = 3;
|
||||
info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
|
||||
chip->ecc.calculate = nand_davinci_calculate_1bit;
|
||||
chip->ecc.correct = nand_davinci_correct_1bit;
|
||||
chip->ecc.hwctl = nand_davinci_hwctl_1bit;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
|
||||
}
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.strength = pdata->ecc_bits;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.strength = pdata->ecc_bits;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -899,7 +899,7 @@ static int nand_davinci_remove(struct platform_device *pdev)
|
|||
int ret;
|
||||
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (info->chip.ecc.placement == NAND_ECC_PLACEMENT_INTERLEAVED)
|
||||
if (chip->ecc.placement == NAND_ECC_PLACEMENT_INTERLEAVED)
|
||||
ecc4_busy = false;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
|
|
|
@ -216,7 +216,7 @@ static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)
|
|||
|
||||
static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
|
||||
{
|
||||
volatile char dummy;
|
||||
volatile char __always_unused dummy;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cycles; i++) {
|
||||
|
@ -703,7 +703,7 @@ static int doc200x_calculate_ecc(struct nand_chip *this, const u_char *dat,
|
|||
struct doc_priv *doc = nand_get_controller_data(this);
|
||||
void __iomem *docptr = doc->virtadr;
|
||||
int i;
|
||||
int emptymatch = 1;
|
||||
int __always_unused emptymatch = 1;
|
||||
|
||||
/* flush the pipeline */
|
||||
if (DoC_is_2000(doc)) {
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/fsl_ifc.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
@ -918,7 +917,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand)
|
|||
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
||||
dev_info(host->dev, "Using 1-bit HW ECC scheme\n");
|
||||
nand->ecc.calculate = fsmc_read_hwecc_ecc1;
|
||||
nand->ecc.correct = nand_correct_data;
|
||||
nand->ecc.correct = rawnand_sw_hamming_correct;
|
||||
nand->ecc.hwctl = fsmc_enable_hwecc;
|
||||
nand->ecc.bytes = 3;
|
||||
nand->ecc.strength = 1;
|
||||
|
@ -942,7 +941,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand)
|
|||
|
||||
/*
|
||||
* Don't set layout for BCH4 SW ECC. This will be
|
||||
* generated later in nand_bch_init() later.
|
||||
* generated later during BCH initialization.
|
||||
*/
|
||||
if (nand->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
|
||||
switch (mtd->oobsize) {
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o
|
||||
gpmi_nand-objs += gpmi-nand.o
|
||||
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand.o
|
||||
|
|
|
@ -149,8 +149,10 @@ static int gpmi_init(struct gpmi_nand_data *this)
|
|||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(this->dev);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(this->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpmi_reset_block(r->gpmi_regs, false);
|
||||
if (ret)
|
||||
|
@ -179,9 +181,11 @@ static int gpmi_init(struct gpmi_nand_data *this)
|
|||
|
||||
/*
|
||||
* Decouple the chip select from dma channel. We use dma0 for all
|
||||
* the chips.
|
||||
* the chips, force all NAND RDY_BUSY inputs to be sourced from
|
||||
* RDY_BUSY0.
|
||||
*/
|
||||
writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET);
|
||||
writel(BM_GPMI_CTRL1_DECOUPLE_CS | BM_GPMI_CTRL1_GANGED_RDYBUSY,
|
||||
r->gpmi_regs + HW_GPMI_CTRL1_SET);
|
||||
|
||||
err_out:
|
||||
pm_runtime_mark_last_busy(this->dev);
|
||||
|
@ -2252,7 +2256,7 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
|
|||
void *buf_read = NULL;
|
||||
const void *buf_write = NULL;
|
||||
bool direct = false;
|
||||
struct completion *completion;
|
||||
struct completion *dma_completion, *bch_completion;
|
||||
unsigned long to;
|
||||
|
||||
if (check_only)
|
||||
|
@ -2263,8 +2267,10 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
|
|||
this->transfers[i].direction = DMA_NONE;
|
||||
|
||||
ret = pm_runtime_get_sync(this->dev);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(this->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This driver currently supports only one NAND chip. Plus, dies share
|
||||
|
@ -2347,22 +2353,24 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
|
|||
this->resources.bch_regs + HW_BCH_FLASH0LAYOUT1);
|
||||
}
|
||||
|
||||
desc->callback = dma_irq_callback;
|
||||
desc->callback_param = this;
|
||||
dma_completion = &this->dma_done;
|
||||
bch_completion = NULL;
|
||||
|
||||
init_completion(dma_completion);
|
||||
|
||||
if (this->bch && buf_read) {
|
||||
writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
|
||||
this->resources.bch_regs + HW_BCH_CTRL_SET);
|
||||
completion = &this->bch_done;
|
||||
} else {
|
||||
desc->callback = dma_irq_callback;
|
||||
desc->callback_param = this;
|
||||
completion = &this->dma_done;
|
||||
bch_completion = &this->bch_done;
|
||||
init_completion(bch_completion);
|
||||
}
|
||||
|
||||
init_completion(completion);
|
||||
|
||||
dmaengine_submit(desc);
|
||||
dma_async_issue_pending(get_dma_chan(this));
|
||||
|
||||
to = wait_for_completion_timeout(completion, msecs_to_jiffies(1000));
|
||||
to = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000));
|
||||
if (!to) {
|
||||
dev_err(this->dev, "DMA timeout, last DMA\n");
|
||||
gpmi_dump_info(this);
|
||||
|
@ -2370,6 +2378,16 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
|
|||
goto unmap;
|
||||
}
|
||||
|
||||
if (this->bch && buf_read) {
|
||||
to = wait_for_completion_timeout(bch_completion, msecs_to_jiffies(1000));
|
||||
if (!to) {
|
||||
dev_err(this->dev, "BCH timeout, last DMA\n");
|
||||
gpmi_dump_info(this);
|
||||
ret = -ETIMEDOUT;
|
||||
goto unmap;
|
||||
}
|
||||
}
|
||||
|
||||
writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
|
||||
this->resources.bch_regs + HW_BCH_CTRL_CLR);
|
||||
gpmi_clear_bch(this);
|
||||
|
@ -2461,43 +2479,25 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
|
|||
}
|
||||
|
||||
static const struct of_device_id gpmi_nand_id_table[] = {
|
||||
{
|
||||
.compatible = "fsl,imx23-gpmi-nand",
|
||||
.data = &gpmi_devdata_imx23,
|
||||
}, {
|
||||
.compatible = "fsl,imx28-gpmi-nand",
|
||||
.data = &gpmi_devdata_imx28,
|
||||
}, {
|
||||
.compatible = "fsl,imx6q-gpmi-nand",
|
||||
.data = &gpmi_devdata_imx6q,
|
||||
}, {
|
||||
.compatible = "fsl,imx6sx-gpmi-nand",
|
||||
.data = &gpmi_devdata_imx6sx,
|
||||
}, {
|
||||
.compatible = "fsl,imx7d-gpmi-nand",
|
||||
.data = &gpmi_devdata_imx7d,
|
||||
}, {}
|
||||
{ .compatible = "fsl,imx23-gpmi-nand", .data = &gpmi_devdata_imx23, },
|
||||
{ .compatible = "fsl,imx28-gpmi-nand", .data = &gpmi_devdata_imx28, },
|
||||
{ .compatible = "fsl,imx6q-gpmi-nand", .data = &gpmi_devdata_imx6q, },
|
||||
{ .compatible = "fsl,imx6sx-gpmi-nand", .data = &gpmi_devdata_imx6sx, },
|
||||
{ .compatible = "fsl,imx7d-gpmi-nand", .data = &gpmi_devdata_imx7d,},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
|
||||
|
||||
static int gpmi_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpmi_nand_data *this;
|
||||
const struct of_device_id *of_id;
|
||||
int ret;
|
||||
|
||||
this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
|
||||
if (!this)
|
||||
return -ENOMEM;
|
||||
|
||||
of_id = of_match_device(gpmi_nand_id_table, &pdev->dev);
|
||||
if (of_id) {
|
||||
this->devdata = of_id->data;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "Failed to find the right device id.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
this->devdata = of_device_get_match_data(&pdev->dev);
|
||||
platform_set_drvdata(pdev, this);
|
||||
this->pdev = pdev;
|
||||
this->dev = &pdev->dev;
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2
|
||||
#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3
|
||||
|
||||
#define BM_GPMI_CTRL1_GANGED_RDYBUSY (1 << 19)
|
||||
#define BM_GPMI_CTRL1_BCH_MODE (1 << 18)
|
||||
|
||||
#define BP_GPMI_CTRL1_DLL_ENABLE 17
|
||||
|
|
|
@ -71,8 +71,6 @@ static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np)
|
|||
if (!pdev || !platform_get_drvdata(pdev))
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
get_device(&pdev->dev);
|
||||
|
||||
ecc = platform_get_drvdata(pdev);
|
||||
clk_prepare_enable(ecc->clk);
|
||||
|
||||
|
|
|
@ -0,0 +1,721 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Copyright (c) 2020 Intel Corporation. */
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define EBU_CLC 0x000
|
||||
#define EBU_CLC_RST 0x00000000u
|
||||
|
||||
#define EBU_ADDR_SEL(n) (0x020 + (n) * 4)
|
||||
/* 5 bits 26:22 included for comparison in the ADDR_SELx */
|
||||
#define EBU_ADDR_MASK(x) ((x) << 4)
|
||||
#define EBU_ADDR_SEL_REGEN 0x1
|
||||
|
||||
#define EBU_BUSCON(n) (0x060 + (n) * 4)
|
||||
#define EBU_BUSCON_CMULT_V4 0x1
|
||||
#define EBU_BUSCON_RECOVC(n) ((n) << 2)
|
||||
#define EBU_BUSCON_HOLDC(n) ((n) << 4)
|
||||
#define EBU_BUSCON_WAITRDC(n) ((n) << 6)
|
||||
#define EBU_BUSCON_WAITWRC(n) ((n) << 8)
|
||||
#define EBU_BUSCON_BCGEN_CS 0x0
|
||||
#define EBU_BUSCON_SETUP_EN BIT(22)
|
||||
#define EBU_BUSCON_ALEC 0xC000
|
||||
|
||||
#define EBU_CON 0x0B0
|
||||
#define EBU_CON_NANDM_EN BIT(0)
|
||||
#define EBU_CON_NANDM_DIS 0x0
|
||||
#define EBU_CON_CSMUX_E_EN BIT(1)
|
||||
#define EBU_CON_ALE_P_LOW BIT(2)
|
||||
#define EBU_CON_CLE_P_LOW BIT(3)
|
||||
#define EBU_CON_CS_P_LOW BIT(4)
|
||||
#define EBU_CON_SE_P_LOW BIT(5)
|
||||
#define EBU_CON_WP_P_LOW BIT(6)
|
||||
#define EBU_CON_PRE_P_LOW BIT(7)
|
||||
#define EBU_CON_IN_CS_S(n) ((n) << 8)
|
||||
#define EBU_CON_OUT_CS_S(n) ((n) << 10)
|
||||
#define EBU_CON_LAT_EN_CS_P ((0x3D) << 18)
|
||||
|
||||
#define EBU_WAIT 0x0B4
|
||||
#define EBU_WAIT_RDBY BIT(0)
|
||||
#define EBU_WAIT_WR_C BIT(3)
|
||||
|
||||
#define HSNAND_CTL1 0x110
|
||||
#define HSNAND_CTL1_ADDR_SHIFT 24
|
||||
|
||||
#define HSNAND_CTL2 0x114
|
||||
#define HSNAND_CTL2_ADDR_SHIFT 8
|
||||
#define HSNAND_CTL2_CYC_N_V5 (0x2 << 16)
|
||||
|
||||
#define HSNAND_INT_MSK_CTL 0x124
|
||||
#define HSNAND_INT_MSK_CTL_WR_C BIT(4)
|
||||
|
||||
#define HSNAND_INT_STA 0x128
|
||||
#define HSNAND_INT_STA_WR_C BIT(4)
|
||||
|
||||
#define HSNAND_CTL 0x130
|
||||
#define HSNAND_CTL_ENABLE_ECC BIT(0)
|
||||
#define HSNAND_CTL_GO BIT(2)
|
||||
#define HSNAND_CTL_CE_SEL_CS(n) BIT(3 + (n))
|
||||
#define HSNAND_CTL_RW_READ 0x0
|
||||
#define HSNAND_CTL_RW_WRITE BIT(10)
|
||||
#define HSNAND_CTL_ECC_OFF_V8TH BIT(11)
|
||||
#define HSNAND_CTL_CKFF_EN 0x0
|
||||
#define HSNAND_CTL_MSG_EN BIT(17)
|
||||
|
||||
#define HSNAND_PARA0 0x13c
|
||||
#define HSNAND_PARA0_PAGE_V8192 0x3
|
||||
#define HSNAND_PARA0_PIB_V256 (0x3 << 4)
|
||||
#define HSNAND_PARA0_BYP_EN_NP 0x0
|
||||
#define HSNAND_PARA0_BYP_DEC_NP 0x0
|
||||
#define HSNAND_PARA0_TYPE_ONFI BIT(18)
|
||||
#define HSNAND_PARA0_ADEP_EN BIT(21)
|
||||
|
||||
#define HSNAND_CMSG_0 0x150
|
||||
#define HSNAND_CMSG_1 0x154
|
||||
|
||||
#define HSNAND_ALE_OFFS BIT(2)
|
||||
#define HSNAND_CLE_OFFS BIT(3)
|
||||
#define HSNAND_CS_OFFS BIT(4)
|
||||
|
||||
#define HSNAND_ECC_OFFSET 0x008
|
||||
|
||||
#define NAND_DATA_IFACE_CHECK_ONLY -1
|
||||
|
||||
#define MAX_CS 2
|
||||
|
||||
#define HZ_PER_MHZ 1000000L
|
||||
#define USEC_PER_SEC 1000000L
|
||||
|
||||
struct ebu_nand_cs {
|
||||
void __iomem *chipaddr;
|
||||
dma_addr_t nand_pa;
|
||||
u32 addr_sel;
|
||||
};
|
||||
|
||||
struct ebu_nand_controller {
|
||||
struct nand_controller controller;
|
||||
struct nand_chip chip;
|
||||
struct device *dev;
|
||||
void __iomem *ebu;
|
||||
void __iomem *hsnand;
|
||||
struct dma_chan *dma_tx;
|
||||
struct dma_chan *dma_rx;
|
||||
struct completion dma_access_complete;
|
||||
unsigned long clk_rate;
|
||||
struct clk *clk;
|
||||
u32 nd_para0;
|
||||
u8 cs_num;
|
||||
struct ebu_nand_cs cs[MAX_CS];
|
||||
};
|
||||
|
||||
static inline struct ebu_nand_controller *nand_to_ebu(struct nand_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct ebu_nand_controller, chip);
|
||||
}
|
||||
|
||||
static int ebu_nand_waitrdy(struct nand_chip *chip, int timeout_ms)
|
||||
{
|
||||
struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
|
||||
u32 status;
|
||||
|
||||
return readl_poll_timeout(ctrl->ebu + EBU_WAIT, status,
|
||||
(status & EBU_WAIT_RDBY) ||
|
||||
(status & EBU_WAIT_WR_C), 20, timeout_ms);
|
||||
}
|
||||
|
||||
static u8 ebu_nand_readb(struct nand_chip *chip)
|
||||
{
|
||||
struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
|
||||
u8 cs_num = ebu_host->cs_num;
|
||||
u8 val;
|
||||
|
||||
val = readb(ebu_host->cs[cs_num].chipaddr + HSNAND_CS_OFFS);
|
||||
ebu_nand_waitrdy(chip, 1000);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void ebu_nand_writeb(struct nand_chip *chip, u32 offset, u8 value)
|
||||
{
|
||||
struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
|
||||
u8 cs_num = ebu_host->cs_num;
|
||||
|
||||
writeb(value, ebu_host->cs[cs_num].chipaddr + offset);
|
||||
ebu_nand_waitrdy(chip, 1000);
|
||||
}
|
||||
|
||||
static void ebu_read_buf(struct nand_chip *chip, u_char *buf, unsigned int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
buf[i] = ebu_nand_readb(chip);
|
||||
}
|
||||
|
||||
static void ebu_write_buf(struct nand_chip *chip, const u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
ebu_nand_writeb(chip, HSNAND_CS_OFFS, buf[i]);
|
||||
}
|
||||
|
||||
static void ebu_nand_disable(struct nand_chip *chip)
|
||||
{
|
||||
struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
|
||||
|
||||
writel(0, ebu_host->ebu + EBU_CON);
|
||||
}
|
||||
|
||||
static void ebu_select_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
|
||||
void __iomem *nand_con = ebu_host->ebu + EBU_CON;
|
||||
u32 cs = ebu_host->cs_num;
|
||||
|
||||
writel(EBU_CON_NANDM_EN | EBU_CON_CSMUX_E_EN | EBU_CON_CS_P_LOW |
|
||||
EBU_CON_SE_P_LOW | EBU_CON_WP_P_LOW | EBU_CON_PRE_P_LOW |
|
||||
EBU_CON_IN_CS_S(cs) | EBU_CON_OUT_CS_S(cs) |
|
||||
EBU_CON_LAT_EN_CS_P, nand_con);
|
||||
}
|
||||
|
||||
static int ebu_nand_set_timings(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
|
||||
unsigned int rate = clk_get_rate(ctrl->clk) / HZ_PER_MHZ;
|
||||
unsigned int period = DIV_ROUND_UP(USEC_PER_SEC, rate);
|
||||
const struct nand_sdr_timings *timings;
|
||||
u32 trecov, thold, twrwait, trdwait;
|
||||
u32 reg = 0;
|
||||
|
||||
timings = nand_get_sdr_timings(conf);
|
||||
if (IS_ERR(timings))
|
||||
return PTR_ERR(timings);
|
||||
|
||||
if (csline == NAND_DATA_IFACE_CHECK_ONLY)
|
||||
return 0;
|
||||
|
||||
trecov = DIV_ROUND_UP(max(timings->tREA_max, timings->tREH_min),
|
||||
period);
|
||||
reg |= EBU_BUSCON_RECOVC(trecov);
|
||||
|
||||
thold = DIV_ROUND_UP(max(timings->tDH_min, timings->tDS_min), period);
|
||||
reg |= EBU_BUSCON_HOLDC(thold);
|
||||
|
||||
trdwait = DIV_ROUND_UP(max(timings->tRC_min, timings->tREH_min),
|
||||
period);
|
||||
reg |= EBU_BUSCON_WAITRDC(trdwait);
|
||||
|
||||
twrwait = DIV_ROUND_UP(max(timings->tWC_min, timings->tWH_min), period);
|
||||
reg |= EBU_BUSCON_WAITWRC(twrwait);
|
||||
|
||||
reg |= EBU_BUSCON_CMULT_V4 | EBU_BUSCON_BCGEN_CS | EBU_BUSCON_ALEC |
|
||||
EBU_BUSCON_SETUP_EN;
|
||||
|
||||
writel(reg, ctrl->ebu + EBU_BUSCON(ctrl->cs_num));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ebu_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->offset = HSNAND_ECC_OFFSET;
|
||||
oobregion->length = chip->ecc.total;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ebu_nand_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->offset = chip->ecc.total + HSNAND_ECC_OFFSET;
|
||||
oobregion->length = mtd->oobsize - oobregion->offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops ebu_nand_ooblayout_ops = {
|
||||
.ecc = ebu_nand_ooblayout_ecc,
|
||||
.free = ebu_nand_ooblayout_free,
|
||||
};
|
||||
|
||||
static void ebu_dma_rx_callback(void *cookie)
|
||||
{
|
||||
struct ebu_nand_controller *ebu_host = cookie;
|
||||
|
||||
dmaengine_terminate_async(ebu_host->dma_rx);
|
||||
|
||||
complete(&ebu_host->dma_access_complete);
|
||||
}
|
||||
|
||||
static void ebu_dma_tx_callback(void *cookie)
|
||||
{
|
||||
struct ebu_nand_controller *ebu_host = cookie;
|
||||
|
||||
dmaengine_terminate_async(ebu_host->dma_tx);
|
||||
|
||||
complete(&ebu_host->dma_access_complete);
|
||||
}
|
||||
|
||||
static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir,
|
||||
const u8 *buf, u32 len)
|
||||
{
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
struct completion *dma_completion;
|
||||
dma_async_tx_callback callback;
|
||||
struct dma_chan *chan;
|
||||
dma_cookie_t cookie;
|
||||
unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
|
||||
dma_addr_t buf_dma;
|
||||
int ret;
|
||||
u32 timeout;
|
||||
|
||||
if (dir == DMA_DEV_TO_MEM) {
|
||||
chan = ebu_host->dma_rx;
|
||||
dma_completion = &ebu_host->dma_access_complete;
|
||||
callback = ebu_dma_rx_callback;
|
||||
} else {
|
||||
chan = ebu_host->dma_tx;
|
||||
dma_completion = &ebu_host->dma_access_complete;
|
||||
callback = ebu_dma_tx_callback;
|
||||
}
|
||||
|
||||
buf_dma = dma_map_single(chan->device->dev, (void *)buf, len, dir);
|
||||
if (dma_mapping_error(chan->device->dev, buf_dma)) {
|
||||
dev_err(ebu_host->dev, "Failed to map DMA buffer\n");
|
||||
ret = -EIO;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
tx = dmaengine_prep_slave_single(chan, buf_dma, len, dir, flags);
|
||||
if (!tx)
|
||||
return -ENXIO;
|
||||
|
||||
tx->callback = callback;
|
||||
tx->callback_param = ebu_host;
|
||||
cookie = tx->tx_submit(tx);
|
||||
|
||||
ret = dma_submit_error(cookie);
|
||||
if (ret) {
|
||||
dev_err(ebu_host->dev, "dma_submit_error %d\n", cookie);
|
||||
ret = -EIO;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
init_completion(dma_completion);
|
||||
dma_async_issue_pending(chan);
|
||||
|
||||
/* Wait DMA to finish the data transfer.*/
|
||||
timeout = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000));
|
||||
if (!timeout) {
|
||||
dev_err(ebu_host->dev, "I/O Error in DMA RX (status %d)\n",
|
||||
dmaengine_tx_status(chan, cookie, NULL));
|
||||
dmaengine_terminate_sync(chan);
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unmap:
|
||||
dma_unmap_single(ebu_host->dev, buf_dma, len, dir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ebu_nand_trigger(struct ebu_nand_controller *ebu_host,
|
||||
int page, u32 cmd)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = cmd | (page & 0xFF) << HSNAND_CTL1_ADDR_SHIFT;
|
||||
writel(val, ebu_host->hsnand + HSNAND_CTL1);
|
||||
val = (page & 0xFFFF00) >> 8 | HSNAND_CTL2_CYC_N_V5;
|
||||
writel(val, ebu_host->hsnand + HSNAND_CTL2);
|
||||
|
||||
writel(ebu_host->nd_para0, ebu_host->hsnand + HSNAND_PARA0);
|
||||
|
||||
/* clear first, will update later */
|
||||
writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_0);
|
||||
writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_1);
|
||||
|
||||
writel(HSNAND_INT_MSK_CTL_WR_C,
|
||||
ebu_host->hsnand + HSNAND_INT_MSK_CTL);
|
||||
|
||||
if (!cmd)
|
||||
val = HSNAND_CTL_RW_READ;
|
||||
else
|
||||
val = HSNAND_CTL_RW_WRITE;
|
||||
|
||||
writel(HSNAND_CTL_MSG_EN | HSNAND_CTL_CKFF_EN |
|
||||
HSNAND_CTL_ECC_OFF_V8TH | HSNAND_CTL_CE_SEL_CS(ebu_host->cs_num) |
|
||||
HSNAND_CTL_ENABLE_ECC | HSNAND_CTL_GO | val,
|
||||
ebu_host->hsnand + HSNAND_CTL);
|
||||
}
|
||||
|
||||
static int ebu_nand_read_page_hwecc(struct nand_chip *chip, u8 *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
|
||||
int ret, reg_data;
|
||||
|
||||
ebu_nand_trigger(ebu_host, page, NAND_CMD_READ0);
|
||||
|
||||
ret = ebu_dma_start(ebu_host, DMA_DEV_TO_MEM, buf, mtd->writesize);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (oob_required)
|
||||
chip->ecc.read_oob(chip, page);
|
||||
|
||||
reg_data = readl(ebu_host->hsnand + HSNAND_CTL);
|
||||
reg_data &= ~HSNAND_CTL_GO;
|
||||
writel(reg_data, ebu_host->hsnand + HSNAND_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ebu_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
|
||||
void __iomem *int_sta = ebu_host->hsnand + HSNAND_INT_STA;
|
||||
int reg_data, ret, val;
|
||||
u32 reg;
|
||||
|
||||
ebu_nand_trigger(ebu_host, page, NAND_CMD_SEQIN);
|
||||
|
||||
ret = ebu_dma_start(ebu_host, DMA_MEM_TO_DEV, buf, mtd->writesize);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (oob_required) {
|
||||
reg = get_unaligned_le32(chip->oob_poi);
|
||||
writel(reg, ebu_host->hsnand + HSNAND_CMSG_0);
|
||||
|
||||
reg = get_unaligned_le32(chip->oob_poi + 4);
|
||||
writel(reg, ebu_host->hsnand + HSNAND_CMSG_1);
|
||||
}
|
||||
|
||||
ret = readl_poll_timeout_atomic(int_sta, val, !(val & HSNAND_INT_STA_WR_C),
|
||||
10, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg_data = readl(ebu_host->hsnand + HSNAND_CTL);
|
||||
reg_data &= ~HSNAND_CTL_GO;
|
||||
writel(reg_data, ebu_host->hsnand + HSNAND_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u8 ecc_strength[] = { 1, 1, 4, 8, 24, 32, 40, 60, };
|
||||
|
||||
static int ebu_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
|
||||
u32 ecc_steps, ecc_bytes, ecc_total, pagesize, pg_per_blk;
|
||||
u32 ecc_strength_ds = chip->ecc.strength;
|
||||
u32 ecc_size = chip->ecc.size;
|
||||
u32 writesize = mtd->writesize;
|
||||
u32 blocksize = mtd->erasesize;
|
||||
int bch_algo, start, val;
|
||||
|
||||
/* Default to an ECC size of 512 */
|
||||
if (!chip->ecc.size)
|
||||
chip->ecc.size = 512;
|
||||
|
||||
switch (ecc_size) {
|
||||
case 512:
|
||||
start = 1;
|
||||
if (!ecc_strength_ds)
|
||||
ecc_strength_ds = 4;
|
||||
break;
|
||||
case 1024:
|
||||
start = 4;
|
||||
if (!ecc_strength_ds)
|
||||
ecc_strength_ds = 32;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* BCH ECC algorithm Settings for number of bits per 512B/1024B */
|
||||
bch_algo = round_up(start + 1, 4);
|
||||
for (val = start; val < bch_algo; val++) {
|
||||
if (ecc_strength_ds == ecc_strength[val])
|
||||
break;
|
||||
}
|
||||
if (val == bch_algo)
|
||||
return -EINVAL;
|
||||
|
||||
if (ecc_strength_ds == 8)
|
||||
ecc_bytes = 14;
|
||||
else
|
||||
ecc_bytes = DIV_ROUND_UP(ecc_strength_ds * fls(8 * ecc_size), 8);
|
||||
|
||||
ecc_steps = writesize / ecc_size;
|
||||
ecc_total = ecc_steps * ecc_bytes;
|
||||
if ((ecc_total + 8) > mtd->oobsize)
|
||||
return -ERANGE;
|
||||
|
||||
chip->ecc.total = ecc_total;
|
||||
pagesize = fls(writesize >> 11);
|
||||
if (pagesize > HSNAND_PARA0_PAGE_V8192)
|
||||
return -ERANGE;
|
||||
|
||||
pg_per_blk = fls((blocksize / writesize) >> 6) / 8;
|
||||
if (pg_per_blk > HSNAND_PARA0_PIB_V256)
|
||||
return -ERANGE;
|
||||
|
||||
ebu_host->nd_para0 = pagesize | pg_per_blk | HSNAND_PARA0_BYP_EN_NP |
|
||||
HSNAND_PARA0_BYP_DEC_NP | HSNAND_PARA0_ADEP_EN |
|
||||
HSNAND_PARA0_TYPE_ONFI | (val << 29);
|
||||
|
||||
mtd_set_ooblayout(mtd, &ebu_nand_ooblayout_ops);
|
||||
chip->ecc.read_page = ebu_nand_read_page_hwecc;
|
||||
chip->ecc.write_page = ebu_nand_write_page_hwecc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ebu_nand_exec_op(struct nand_chip *chip,
|
||||
const struct nand_operation *op, bool check_only)
|
||||
{
|
||||
const struct nand_op_instr *instr = NULL;
|
||||
unsigned int op_id;
|
||||
int i, timeout_ms, ret = 0;
|
||||
|
||||
if (check_only)
|
||||
return 0;
|
||||
|
||||
ebu_select_chip(chip);
|
||||
for (op_id = 0; op_id < op->ninstrs; op_id++) {
|
||||
instr = &op->instrs[op_id];
|
||||
|
||||
switch (instr->type) {
|
||||
case NAND_OP_CMD_INSTR:
|
||||
ebu_nand_writeb(chip, HSNAND_CLE_OFFS | HSNAND_CS_OFFS,
|
||||
instr->ctx.cmd.opcode);
|
||||
break;
|
||||
|
||||
case NAND_OP_ADDR_INSTR:
|
||||
for (i = 0; i < instr->ctx.addr.naddrs; i++)
|
||||
ebu_nand_writeb(chip,
|
||||
HSNAND_ALE_OFFS | HSNAND_CS_OFFS,
|
||||
instr->ctx.addr.addrs[i]);
|
||||
break;
|
||||
|
||||
case NAND_OP_DATA_IN_INSTR:
|
||||
ebu_read_buf(chip, instr->ctx.data.buf.in,
|
||||
instr->ctx.data.len);
|
||||
break;
|
||||
|
||||
case NAND_OP_DATA_OUT_INSTR:
|
||||
ebu_write_buf(chip, instr->ctx.data.buf.out,
|
||||
instr->ctx.data.len);
|
||||
break;
|
||||
|
||||
case NAND_OP_WAITRDY_INSTR:
|
||||
timeout_ms = instr->ctx.waitrdy.timeout_ms * 1000;
|
||||
ret = ebu_nand_waitrdy(chip, timeout_ms);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops ebu_nand_controller_ops = {
|
||||
.attach_chip = ebu_nand_attach_chip,
|
||||
.setup_interface = ebu_nand_set_timings,
|
||||
.exec_op = ebu_nand_exec_op,
|
||||
};
|
||||
|
||||
static void ebu_dma_cleanup(struct ebu_nand_controller *ebu_host)
|
||||
{
|
||||
if (ebu_host->dma_rx)
|
||||
dma_release_channel(ebu_host->dma_rx);
|
||||
|
||||
if (ebu_host->dma_tx)
|
||||
dma_release_channel(ebu_host->dma_tx);
|
||||
}
|
||||
|
||||
static int ebu_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ebu_nand_controller *ebu_host;
|
||||
struct nand_chip *nand;
|
||||
struct mtd_info *mtd = NULL;
|
||||
struct resource *res;
|
||||
char *resname;
|
||||
int ret;
|
||||
u32 cs;
|
||||
|
||||
ebu_host = devm_kzalloc(dev, sizeof(*ebu_host), GFP_KERNEL);
|
||||
if (!ebu_host)
|
||||
return -ENOMEM;
|
||||
|
||||
ebu_host->dev = dev;
|
||||
nand_controller_init(&ebu_host->controller);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ebunand");
|
||||
ebu_host->ebu = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ebu_host->ebu))
|
||||
return PTR_ERR(ebu_host->ebu);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsnand");
|
||||
ebu_host->hsnand = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ebu_host->hsnand))
|
||||
return PTR_ERR(ebu_host->hsnand);
|
||||
|
||||
ret = device_property_read_u32(dev, "reg", &cs);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get chip select: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ebu_host->cs_num = cs;
|
||||
|
||||
resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", cs);
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname);
|
||||
ebu_host->cs[cs].chipaddr = devm_ioremap_resource(dev, res);
|
||||
ebu_host->cs[cs].nand_pa = res->start;
|
||||
if (IS_ERR(ebu_host->cs[cs].chipaddr))
|
||||
return PTR_ERR(ebu_host->cs[cs].chipaddr);
|
||||
|
||||
ebu_host->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(ebu_host->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(ebu_host->clk),
|
||||
"failed to get clock\n");
|
||||
|
||||
ret = clk_prepare_enable(ebu_host->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ebu_host->clk_rate = clk_get_rate(ebu_host->clk);
|
||||
|
||||
ebu_host->dma_tx = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(ebu_host->dma_tx))
|
||||
return dev_err_probe(dev, PTR_ERR(ebu_host->dma_tx),
|
||||
"failed to request DMA tx chan!.\n");
|
||||
|
||||
ebu_host->dma_rx = dma_request_chan(dev, "rx");
|
||||
if (IS_ERR(ebu_host->dma_rx))
|
||||
return dev_err_probe(dev, PTR_ERR(ebu_host->dma_rx),
|
||||
"failed to request DMA rx chan!.\n");
|
||||
|
||||
resname = devm_kasprintf(dev, GFP_KERNEL, "addr_sel%d", cs);
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
ebu_host->cs[cs].addr_sel = res->start;
|
||||
writel(ebu_host->cs[cs].addr_sel | EBU_ADDR_MASK(5) | EBU_ADDR_SEL_REGEN,
|
||||
ebu_host->ebu + EBU_ADDR_SEL(cs));
|
||||
|
||||
nand_set_flash_node(&ebu_host->chip, dev->of_node);
|
||||
if (!mtd->name) {
|
||||
dev_err(ebu_host->dev, "NAND label property is mandatory\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mtd = nand_to_mtd(&ebu_host->chip);
|
||||
mtd->dev.parent = dev;
|
||||
ebu_host->dev = dev;
|
||||
|
||||
platform_set_drvdata(pdev, ebu_host);
|
||||
nand_set_controller_data(&ebu_host->chip, ebu_host);
|
||||
|
||||
nand = &ebu_host->chip;
|
||||
nand->controller = &ebu_host->controller;
|
||||
nand->controller->ops = &ebu_nand_controller_ops;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
ret = nand_scan(&ebu_host->chip, 1);
|
||||
if (ret)
|
||||
goto err_cleanup_dma;
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret)
|
||||
goto err_clean_nand;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clean_nand:
|
||||
nand_cleanup(&ebu_host->chip);
|
||||
err_cleanup_dma:
|
||||
ebu_dma_cleanup(ebu_host);
|
||||
clk_disable_unprepare(ebu_host->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ebu_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ebu_nand_controller *ebu_host = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = mtd_device_unregister(nand_to_mtd(&ebu_host->chip));
|
||||
WARN_ON(ret);
|
||||
nand_cleanup(&ebu_host->chip);
|
||||
ebu_nand_disable(&ebu_host->chip);
|
||||
ebu_dma_cleanup(ebu_host);
|
||||
clk_disable_unprepare(ebu_host->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ebu_nand_match[] = {
|
||||
{ .compatible = "intel,nand-controller" },
|
||||
{ .compatible = "intel,lgm-ebunand" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ebu_nand_match);
|
||||
|
||||
static struct platform_driver ebu_nand_driver = {
|
||||
.probe = ebu_nand_probe,
|
||||
.remove = ebu_nand_remove,
|
||||
.driver = {
|
||||
.name = "intel-nand-controller",
|
||||
.of_match_table = ebu_nand_match,
|
||||
},
|
||||
|
||||
};
|
||||
module_platform_driver(ebu_nand_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel's LGM External Bus NAND Controller driver");
|
|
@ -31,7 +31,6 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
|
||||
#define DRV_NAME "lpc32xx_mlc"
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
@ -803,7 +802,7 @@ static int lpc32xx_nand_attach_chip(struct nand_chip *chip)
|
|||
chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome;
|
||||
chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome;
|
||||
chip->ecc.calculate = lpc32xx_nand_ecc_calculate;
|
||||
chip->ecc.correct = nand_correct_data;
|
||||
chip->ecc.correct = rawnand_sw_hamming_correct;
|
||||
chip->ecc.hwctl = lpc32xx_nand_ecc_enable;
|
||||
|
||||
/*
|
||||
|
|
|
@ -2678,12 +2678,6 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
|
|||
mtd = nand_to_mtd(chip);
|
||||
mtd->dev.parent = dev;
|
||||
|
||||
/*
|
||||
* Default to HW ECC engine mode. If the nand-ecc-mode property is given
|
||||
* in the DT node, this entry will be overwritten in nand_scan_ident().
|
||||
*/
|
||||
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
|
||||
|
||||
/*
|
||||
* Save a reference value for timing registers before
|
||||
* ->setup_interface() is called.
|
||||
|
|
|
@ -510,7 +510,7 @@ static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf,
|
|||
}
|
||||
|
||||
static void meson_nfc_dma_buffer_release(struct nand_chip *nand,
|
||||
int infolen, int datalen,
|
||||
int datalen, int infolen,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct meson_nfc *nfc = nand_get_controller_data(nand);
|
||||
|
@ -1044,9 +1044,12 @@ static int meson_nfc_clk_init(struct meson_nfc *nfc)
|
|||
|
||||
ret = clk_set_rate(nfc->device_clk, 24000000);
|
||||
if (ret)
|
||||
goto err_phase_rx;
|
||||
goto err_disable_rx;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_rx:
|
||||
clk_disable_unprepare(nfc->phase_rx);
|
||||
err_phase_rx:
|
||||
clk_disable_unprepare(nfc->phase_tx);
|
||||
err_phase_tx:
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_data/mtd-mxc_nand.h>
|
||||
|
||||
#define DRIVER_NAME "mxc_nand"
|
||||
|
||||
|
@ -184,7 +183,6 @@ struct mxc_nand_host {
|
|||
unsigned int buf_start;
|
||||
|
||||
const struct mxc_nand_devtype_data *devtype_data;
|
||||
struct mxc_nand_platform_data pdata;
|
||||
};
|
||||
|
||||
static const char * const part_probes[] = {
|
||||
|
@ -1611,70 +1609,16 @@ static inline int is_imx53_nfc(struct mxc_nand_host *host)
|
|||
return host->devtype_data == &imx53_nand_devtype_data;
|
||||
}
|
||||
|
||||
static const struct platform_device_id mxcnd_devtype[] = {
|
||||
{
|
||||
.name = "imx21-nand",
|
||||
.driver_data = (kernel_ulong_t) &imx21_nand_devtype_data,
|
||||
}, {
|
||||
.name = "imx27-nand",
|
||||
.driver_data = (kernel_ulong_t) &imx27_nand_devtype_data,
|
||||
}, {
|
||||
.name = "imx25-nand",
|
||||
.driver_data = (kernel_ulong_t) &imx25_nand_devtype_data,
|
||||
}, {
|
||||
.name = "imx51-nand",
|
||||
.driver_data = (kernel_ulong_t) &imx51_nand_devtype_data,
|
||||
}, {
|
||||
.name = "imx53-nand",
|
||||
.driver_data = (kernel_ulong_t) &imx53_nand_devtype_data,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, mxcnd_devtype);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mxcnd_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx21-nand",
|
||||
.data = &imx21_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx27-nand",
|
||||
.data = &imx27_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx25-nand",
|
||||
.data = &imx25_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx51-nand",
|
||||
.data = &imx51_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx53-nand",
|
||||
.data = &imx53_nand_devtype_data,
|
||||
},
|
||||
{ .compatible = "fsl,imx21-nand", .data = &imx21_nand_devtype_data, },
|
||||
{ .compatible = "fsl,imx27-nand", .data = &imx27_nand_devtype_data, },
|
||||
{ .compatible = "fsl,imx25-nand", .data = &imx25_nand_devtype_data, },
|
||||
{ .compatible = "fsl,imx51-nand", .data = &imx51_nand_devtype_data, },
|
||||
{ .compatible = "fsl,imx53-nand", .data = &imx53_nand_devtype_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxcnd_dt_ids);
|
||||
|
||||
static int mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
{
|
||||
struct device_node *np = host->dev->of_node;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxcnd_dt_ids, host->dev);
|
||||
|
||||
if (!np)
|
||||
return 1;
|
||||
|
||||
host->devtype_data = of_id->data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mxcnd_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
@ -1800,20 +1744,7 @@ static int mxcnd_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(host->clk))
|
||||
return PTR_ERR(host->clk);
|
||||
|
||||
err = mxcnd_probe_dt(host);
|
||||
if (err > 0) {
|
||||
struct mxc_nand_platform_data *pdata =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
if (pdata) {
|
||||
host->pdata = *pdata;
|
||||
host->devtype_data = (struct mxc_nand_devtype_data *)
|
||||
pdev->id_entry->driver_data;
|
||||
} else {
|
||||
err = -ENODEV;
|
||||
}
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
host->devtype_data = device_get_match_data(&pdev->dev);
|
||||
|
||||
if (!host->devtype_data->setup_interface)
|
||||
this->options |= NAND_KEEP_TIMINGS;
|
||||
|
@ -1843,14 +1774,6 @@ static int mxcnd_probe(struct platform_device *pdev)
|
|||
|
||||
this->legacy.select_chip = host->devtype_data->select_chip;
|
||||
|
||||
/* NAND bus width determines access functions used by upper layer */
|
||||
if (host->pdata.width == 2)
|
||||
this->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
/* update flash based bbt */
|
||||
if (host->pdata.flash_bbt)
|
||||
this->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
|
||||
init_completion(&host->op_completion);
|
||||
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
|
@ -1891,9 +1814,7 @@ static int mxcnd_probe(struct platform_device *pdev)
|
|||
goto escan;
|
||||
|
||||
/* Register the partitions */
|
||||
err = mtd_device_parse_register(mtd, part_probes, NULL,
|
||||
host->pdata.parts,
|
||||
host->pdata.nr_parts);
|
||||
err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
|
||||
if (err)
|
||||
goto cleanup_nand;
|
||||
|
||||
|
@ -1930,7 +1851,6 @@ static struct platform_driver mxcnd_driver = {
|
|||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mxcnd_dt_ids),
|
||||
},
|
||||
.id_table = mxcnd_devtype,
|
||||
.probe = mxcnd_probe,
|
||||
.remove = mxcnd_remove,
|
||||
};
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "internals.h"
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include <linux/mtd/nand-ecc-sw-bch.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -5139,6 +5139,118 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip)
|
|||
kfree(chip->parameters.onfi);
|
||||
}
|
||||
|
||||
int rawnand_sw_hamming_init(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_ecc_sw_hamming_conf *engine_conf;
|
||||
struct nand_device *base = &chip->base;
|
||||
int ret;
|
||||
|
||||
base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
|
||||
base->ecc.user_conf.algo = NAND_ECC_ALGO_HAMMING;
|
||||
base->ecc.user_conf.strength = chip->ecc.strength;
|
||||
base->ecc.user_conf.step_size = chip->ecc.size;
|
||||
|
||||
ret = nand_ecc_sw_hamming_init_ctx(base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
engine_conf = base->ecc.ctx.priv;
|
||||
|
||||
if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER)
|
||||
engine_conf->sm_order = true;
|
||||
|
||||
chip->ecc.size = base->ecc.ctx.conf.step_size;
|
||||
chip->ecc.strength = base->ecc.ctx.conf.strength;
|
||||
chip->ecc.total = base->ecc.ctx.total;
|
||||
chip->ecc.steps = engine_conf->nsteps;
|
||||
chip->ecc.bytes = engine_conf->code_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rawnand_sw_hamming_init);
|
||||
|
||||
int rawnand_sw_hamming_calculate(struct nand_chip *chip,
|
||||
const unsigned char *buf,
|
||||
unsigned char *code)
|
||||
{
|
||||
struct nand_device *base = &chip->base;
|
||||
|
||||
return nand_ecc_sw_hamming_calculate(base, buf, code);
|
||||
}
|
||||
EXPORT_SYMBOL(rawnand_sw_hamming_calculate);
|
||||
|
||||
int rawnand_sw_hamming_correct(struct nand_chip *chip,
|
||||
unsigned char *buf,
|
||||
unsigned char *read_ecc,
|
||||
unsigned char *calc_ecc)
|
||||
{
|
||||
struct nand_device *base = &chip->base;
|
||||
|
||||
return nand_ecc_sw_hamming_correct(base, buf, read_ecc, calc_ecc);
|
||||
}
|
||||
EXPORT_SYMBOL(rawnand_sw_hamming_correct);
|
||||
|
||||
void rawnand_sw_hamming_cleanup(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_device *base = &chip->base;
|
||||
|
||||
nand_ecc_sw_hamming_cleanup_ctx(base);
|
||||
}
|
||||
EXPORT_SYMBOL(rawnand_sw_hamming_cleanup);
|
||||
|
||||
int rawnand_sw_bch_init(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_device *base = &chip->base;
|
||||
struct nand_ecc_sw_bch_conf *engine_conf;
|
||||
int ret;
|
||||
|
||||
base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
|
||||
base->ecc.user_conf.algo = NAND_ECC_ALGO_BCH;
|
||||
base->ecc.user_conf.step_size = chip->ecc.size;
|
||||
base->ecc.user_conf.strength = chip->ecc.strength;
|
||||
|
||||
ret = nand_ecc_sw_bch_init_ctx(base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
engine_conf = base->ecc.ctx.priv;
|
||||
|
||||
chip->ecc.size = base->ecc.ctx.conf.step_size;
|
||||
chip->ecc.strength = base->ecc.ctx.conf.strength;
|
||||
chip->ecc.total = base->ecc.ctx.total;
|
||||
chip->ecc.steps = engine_conf->nsteps;
|
||||
chip->ecc.bytes = engine_conf->code_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rawnand_sw_bch_init);
|
||||
|
||||
static int rawnand_sw_bch_calculate(struct nand_chip *chip,
|
||||
const unsigned char *buf,
|
||||
unsigned char *code)
|
||||
{
|
||||
struct nand_device *base = &chip->base;
|
||||
|
||||
return nand_ecc_sw_bch_calculate(base, buf, code);
|
||||
}
|
||||
|
||||
int rawnand_sw_bch_correct(struct nand_chip *chip, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
{
|
||||
struct nand_device *base = &chip->base;
|
||||
|
||||
return nand_ecc_sw_bch_correct(base, buf, read_ecc, calc_ecc);
|
||||
}
|
||||
EXPORT_SYMBOL(rawnand_sw_bch_correct);
|
||||
|
||||
void rawnand_sw_bch_cleanup(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_device *base = &chip->base;
|
||||
|
||||
nand_ecc_sw_bch_cleanup_ctx(base);
|
||||
}
|
||||
EXPORT_SYMBOL(rawnand_sw_bch_cleanup);
|
||||
|
||||
static int nand_set_ecc_on_host_ops(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
@ -5203,14 +5315,15 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
|
|||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_device *nanddev = mtd_to_nanddev(mtd);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(ecc->engine_type != NAND_ECC_ENGINE_TYPE_SOFT))
|
||||
return -EINVAL;
|
||||
|
||||
switch (ecc->algo) {
|
||||
case NAND_ECC_ALGO_HAMMING:
|
||||
ecc->calculate = nand_calculate_ecc;
|
||||
ecc->correct = nand_correct_data;
|
||||
ecc->calculate = rawnand_sw_hamming_calculate;
|
||||
ecc->correct = rawnand_sw_hamming_correct;
|
||||
ecc->read_page = nand_read_page_swecc;
|
||||
ecc->read_subpage = nand_read_subpage;
|
||||
ecc->write_page = nand_write_page_swecc;
|
||||
|
@ -5228,14 +5341,20 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
|
|||
if (IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC))
|
||||
ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
|
||||
|
||||
ret = rawnand_sw_hamming_init(chip);
|
||||
if (ret) {
|
||||
WARN(1, "Hamming ECC initialization failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
case NAND_ECC_ALGO_BCH:
|
||||
if (!mtd_nand_has_bch()) {
|
||||
if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) {
|
||||
WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ecc->calculate = nand_bch_calculate_ecc;
|
||||
ecc->correct = nand_bch_correct_data;
|
||||
ecc->calculate = rawnand_sw_bch_calculate;
|
||||
ecc->correct = rawnand_sw_bch_correct;
|
||||
ecc->read_page = nand_read_page_swecc;
|
||||
ecc->read_subpage = nand_read_subpage;
|
||||
ecc->write_page = nand_write_page_swecc;
|
||||
|
@ -5246,56 +5365,21 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
|
|||
ecc->read_oob = nand_read_oob_std;
|
||||
ecc->write_oob = nand_write_oob_std;
|
||||
|
||||
/*
|
||||
* Board driver should supply ecc.size and ecc.strength
|
||||
* values to select how many bits are correctable.
|
||||
* Otherwise, default to 4 bits for large page devices.
|
||||
*/
|
||||
if (!ecc->size && (mtd->oobsize >= 64)) {
|
||||
ecc->size = 512;
|
||||
ecc->strength = 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* if no ecc placement scheme was provided pickup the default
|
||||
* large page one.
|
||||
*/
|
||||
if (!mtd->ooblayout) {
|
||||
/* handle large page devices only */
|
||||
if (mtd->oobsize < 64) {
|
||||
WARN(1, "OOB layout is required when using software BCH on small pages\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout());
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* We can only maximize ECC config when the default layout is
|
||||
* used, otherwise we don't know how many bytes can really be
|
||||
* used.
|
||||
*/
|
||||
if (mtd->ooblayout == nand_get_large_page_ooblayout() &&
|
||||
nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
|
||||
int steps, bytes;
|
||||
if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH &&
|
||||
mtd->ooblayout != nand_get_large_page_ooblayout())
|
||||
nanddev->ecc.user_conf.flags &= ~NAND_ECC_MAXIMIZE_STRENGTH;
|
||||
|
||||
/* Always prefer 1k blocks over 512bytes ones */
|
||||
ecc->size = 1024;
|
||||
steps = mtd->writesize / ecc->size;
|
||||
|
||||
/* Reserve 2 bytes for the BBM */
|
||||
bytes = (mtd->oobsize - 2) / steps;
|
||||
ecc->strength = bytes * 8 / fls(8 * ecc->size);
|
||||
}
|
||||
|
||||
/* See nand_bch_init() for details. */
|
||||
ecc->bytes = 0;
|
||||
ecc->priv = nand_bch_init(mtd);
|
||||
if (!ecc->priv) {
|
||||
ret = rawnand_sw_bch_init(chip);
|
||||
if (ret) {
|
||||
WARN(1, "BCH ECC initialization failed!\n");
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
default:
|
||||
WARN(1, "Unsupported ECC algorithm!\n");
|
||||
|
@ -5639,7 +5723,9 @@ static int nand_scan_tail(struct nand_chip *chip)
|
|||
*/
|
||||
if (!mtd->ooblayout &&
|
||||
!(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
|
||||
ecc->algo == NAND_ECC_ALGO_BCH)) {
|
||||
ecc->algo == NAND_ECC_ALGO_BCH) &&
|
||||
!(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
|
||||
ecc->algo == NAND_ECC_ALGO_HAMMING)) {
|
||||
switch (mtd->oobsize) {
|
||||
case 8:
|
||||
case 16:
|
||||
|
@ -5756,15 +5842,18 @@ static int nand_scan_tail(struct nand_chip *chip)
|
|||
* Set the number of read / write steps for one page depending on ECC
|
||||
* mode.
|
||||
*/
|
||||
ecc->steps = mtd->writesize / ecc->size;
|
||||
if (!ecc->steps)
|
||||
ecc->steps = mtd->writesize / ecc->size;
|
||||
if (ecc->steps * ecc->size != mtd->writesize) {
|
||||
WARN(1, "Invalid ECC parameters\n");
|
||||
ret = -EINVAL;
|
||||
goto err_nand_manuf_cleanup;
|
||||
}
|
||||
|
||||
ecc->total = ecc->steps * ecc->bytes;
|
||||
chip->base.ecc.ctx.total = ecc->total;
|
||||
if (!ecc->total) {
|
||||
ecc->total = ecc->steps * ecc->bytes;
|
||||
chip->base.ecc.ctx.total = ecc->total;
|
||||
}
|
||||
|
||||
if (ecc->total > mtd->oobsize) {
|
||||
WARN(1, "Total number of ECC bytes exceeded oobsize\n");
|
||||
|
@ -5953,9 +6042,12 @@ EXPORT_SYMBOL(nand_scan_with_ids);
|
|||
*/
|
||||
void nand_cleanup(struct nand_chip *chip)
|
||||
{
|
||||
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
|
||||
chip->ecc.algo == NAND_ECC_ALGO_BCH)
|
||||
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
|
||||
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT) {
|
||||
if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING)
|
||||
rawnand_sw_hamming_cleanup(chip);
|
||||
else if (chip->ecc.algo == NAND_ECC_ALGO_BCH)
|
||||
rawnand_sw_bch_cleanup(chip);
|
||||
}
|
||||
|
||||
nanddev_cleanup(&chip->base);
|
||||
|
||||
|
|
|
@ -1087,7 +1087,7 @@ static int nand_update_bbt(struct nand_chip *this, loff_t offs)
|
|||
}
|
||||
|
||||
/**
|
||||
* mark_bbt_regions - [GENERIC] mark the bad block table regions
|
||||
* mark_bbt_region - [GENERIC] mark the bad block table regions
|
||||
* @this: the NAND device
|
||||
* @td: bad block table descriptor
|
||||
*
|
||||
|
|
|
@ -1,219 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* This file provides ECC correction for more than 1 bit per block of data,
|
||||
* using binary BCH codes. It relies on the generic BCH library lib/bch.c.
|
||||
*
|
||||
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/bch.h>
|
||||
|
||||
/**
|
||||
* struct nand_bch_control - private NAND BCH control structure
|
||||
* @bch: BCH control structure
|
||||
* @errloc: error location array
|
||||
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
|
||||
*/
|
||||
struct nand_bch_control {
|
||||
struct bch_control *bch;
|
||||
unsigned int *errloc;
|
||||
unsigned char *eccmask;
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
|
||||
* @chip: NAND chip object
|
||||
* @buf: input buffer with raw data
|
||||
* @code: output buffer with ECC
|
||||
*/
|
||||
int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf,
|
||||
unsigned char *code)
|
||||
{
|
||||
struct nand_bch_control *nbc = chip->ecc.priv;
|
||||
unsigned int i;
|
||||
|
||||
memset(code, 0, chip->ecc.bytes);
|
||||
bch_encode(nbc->bch, buf, chip->ecc.size, code);
|
||||
|
||||
/* apply mask so that an erased page is a valid codeword */
|
||||
for (i = 0; i < chip->ecc.bytes; i++)
|
||||
code[i] ^= nbc->eccmask[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_calculate_ecc);
|
||||
|
||||
/**
|
||||
* nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
|
||||
* @chip: NAND chip object
|
||||
* @buf: raw data read from the chip
|
||||
* @read_ecc: ECC from the chip
|
||||
* @calc_ecc: the ECC calculated from raw data
|
||||
*
|
||||
* Detect and correct bit errors for a data byte block
|
||||
*/
|
||||
int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
{
|
||||
struct nand_bch_control *nbc = chip->ecc.priv;
|
||||
unsigned int *errloc = nbc->errloc;
|
||||
int i, count;
|
||||
|
||||
count = bch_decode(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
|
||||
NULL, errloc);
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
if (errloc[i] < (chip->ecc.size*8))
|
||||
/* error is located in data, correct it */
|
||||
buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
|
||||
/* else error in ecc, no action needed */
|
||||
|
||||
pr_debug("%s: corrected bitflip %u\n", __func__,
|
||||
errloc[i]);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
pr_err("ecc unrecoverable error\n");
|
||||
count = -EBADMSG;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_correct_data);
|
||||
|
||||
/**
|
||||
* nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
|
||||
* @mtd: MTD block structure
|
||||
*
|
||||
* Returns:
|
||||
* a pointer to a new NAND BCH control structure, or NULL upon failure
|
||||
*
|
||||
* Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
|
||||
* are used to compute BCH parameters m (Galois field order) and t (error
|
||||
* correction capability). @eccbytes should be equal to the number of bytes
|
||||
* required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
|
||||
*
|
||||
* Example: to configure 4 bit correction per 512 bytes, you should pass
|
||||
* @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
|
||||
* @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
|
||||
*/
|
||||
struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
unsigned int m, t, eccsteps, i;
|
||||
struct nand_bch_control *nbc = NULL;
|
||||
unsigned char *erased_page;
|
||||
unsigned int eccsize = nand->ecc.size;
|
||||
unsigned int eccbytes = nand->ecc.bytes;
|
||||
unsigned int eccstrength = nand->ecc.strength;
|
||||
|
||||
if (!eccbytes && eccstrength) {
|
||||
eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
|
||||
nand->ecc.bytes = eccbytes;
|
||||
}
|
||||
|
||||
if (!eccsize || !eccbytes) {
|
||||
pr_warn("ecc parameters not supplied\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
m = fls(1+8*eccsize);
|
||||
t = (eccbytes*8)/m;
|
||||
|
||||
nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
|
||||
if (!nbc)
|
||||
goto fail;
|
||||
|
||||
nbc->bch = bch_init(m, t, 0, false);
|
||||
if (!nbc->bch)
|
||||
goto fail;
|
||||
|
||||
/* verify that eccbytes has the expected value */
|
||||
if (nbc->bch->ecc_bytes != eccbytes) {
|
||||
pr_warn("invalid eccbytes %u, should be %u\n",
|
||||
eccbytes, nbc->bch->ecc_bytes);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
eccsteps = mtd->writesize/eccsize;
|
||||
|
||||
/* Check that we have an oob layout description. */
|
||||
if (!mtd->ooblayout) {
|
||||
pr_warn("missing oob scheme");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* sanity checks */
|
||||
if (8*(eccsize+eccbytes) >= (1 << m)) {
|
||||
pr_warn("eccsize %u is too large\n", eccsize);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(),
|
||||
* which is called by mtd_ooblayout_count_eccbytes().
|
||||
* Make sure they are properly initialized before calling
|
||||
* mtd_ooblayout_count_eccbytes().
|
||||
* FIXME: we should probably rework the sequencing in nand_scan_tail()
|
||||
* to avoid setting those fields twice.
|
||||
*/
|
||||
nand->ecc.steps = eccsteps;
|
||||
nand->ecc.total = eccsteps * eccbytes;
|
||||
nand->base.ecc.ctx.total = nand->ecc.total;
|
||||
if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
|
||||
pr_warn("invalid ecc layout\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL);
|
||||
nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL);
|
||||
if (!nbc->eccmask || !nbc->errloc)
|
||||
goto fail;
|
||||
/*
|
||||
* compute and store the inverted ecc of an erased ecc block
|
||||
*/
|
||||
erased_page = kmalloc(eccsize, GFP_KERNEL);
|
||||
if (!erased_page)
|
||||
goto fail;
|
||||
|
||||
memset(erased_page, 0xff, eccsize);
|
||||
bch_encode(nbc->bch, erased_page, eccsize, nbc->eccmask);
|
||||
kfree(erased_page);
|
||||
|
||||
for (i = 0; i < eccbytes; i++)
|
||||
nbc->eccmask[i] ^= 0xff;
|
||||
|
||||
if (!eccstrength)
|
||||
nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
|
||||
|
||||
return nbc;
|
||||
fail:
|
||||
nand_bch_free(nbc);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_init);
|
||||
|
||||
/**
|
||||
* nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
|
||||
* @nbc: NAND BCH control structure
|
||||
*/
|
||||
void nand_bch_free(struct nand_bch_control *nbc)
|
||||
{
|
||||
if (nbc) {
|
||||
bch_free(nbc->bch);
|
||||
kfree(nbc->errloc);
|
||||
kfree(nbc->eccmask);
|
||||
kfree(nbc);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_free);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
|
||||
MODULE_DESCRIPTION("NAND software BCH ECC support");
|
|
@ -192,9 +192,10 @@ static void panic_nand_wait_ready(struct nand_chip *chip, unsigned long timeo)
|
|||
*/
|
||||
void nand_wait_ready(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
unsigned long timeo = 400;
|
||||
|
||||
if (in_interrupt() || oops_in_progress)
|
||||
if (mtd->oops_panic_write)
|
||||
return panic_nand_wait_ready(chip, timeo);
|
||||
|
||||
/* Wait until command is processed or timeout occurs */
|
||||
|
@ -531,7 +532,7 @@ EXPORT_SYMBOL(nand_get_set_features_notsupp);
|
|||
*/
|
||||
static int nand_wait(struct nand_chip *chip)
|
||||
{
|
||||
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
unsigned long timeo = 400;
|
||||
u8 status;
|
||||
int ret;
|
||||
|
@ -546,9 +547,9 @@ static int nand_wait(struct nand_chip *chip)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (in_interrupt() || oops_in_progress)
|
||||
if (mtd->oops_panic_write) {
|
||||
panic_nand_wait(chip, timeo);
|
||||
else {
|
||||
} else {
|
||||
timeo = jiffies + msecs_to_jiffies(timeo);
|
||||
do {
|
||||
if (chip->legacy.dev_ready) {
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/list.h>
|
||||
|
@ -2214,7 +2213,7 @@ static int ns_attach_chip(struct nand_chip *chip)
|
|||
if (!bch)
|
||||
return 0;
|
||||
|
||||
if (!mtd_nand_has_bch()) {
|
||||
if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) {
|
||||
NS_ERR("BCH ECC support is disabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/ndfc.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -146,7 +145,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
|
|||
chip->controller = &ndfc->ndfc_control;
|
||||
chip->legacy.read_buf = ndfc_read_buf;
|
||||
chip->legacy.write_buf = ndfc_write_buf;
|
||||
chip->ecc.correct = nand_correct_data;
|
||||
chip->ecc.correct = rawnand_sw_hamming_correct;
|
||||
chip->ecc.hwctl = ndfc_enable_hwecc;
|
||||
chip->ecc.calculate = ndfc_calculate_ecc;
|
||||
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/platform_data/elm.h>
|
||||
|
||||
#include <linux/omap-gpmc.h>
|
||||
|
@ -185,6 +184,7 @@ static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
|
|||
* @dma_mode: dma mode enable (1) or disable (0)
|
||||
* @u32_count: number of bytes to be transferred
|
||||
* @is_write: prefetch read(0) or write post(1) mode
|
||||
* @info: NAND device structure containing platform data
|
||||
*/
|
||||
static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode,
|
||||
unsigned int u32_count, int is_write, struct omap_nand_info *info)
|
||||
|
@ -214,7 +214,7 @@ static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* omap_prefetch_reset - disables and stops the prefetch engine
|
||||
*/
|
||||
static int omap_prefetch_reset(int cs, struct omap_nand_info *info)
|
||||
|
@ -939,7 +939,7 @@ static int omap_calculate_ecc(struct nand_chip *chip, const u_char *dat,
|
|||
|
||||
/**
|
||||
* omap_enable_hwecc - This function enables the hardware ecc functionality
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip object
|
||||
* @mode: Read/Write mode
|
||||
*/
|
||||
static void omap_enable_hwecc(struct nand_chip *chip, int mode)
|
||||
|
@ -1009,7 +1009,7 @@ static int omap_wait(struct nand_chip *this)
|
|||
|
||||
/**
|
||||
* omap_dev_ready - checks the NAND Ready GPIO line
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip object
|
||||
*
|
||||
* Returns true if ready and false if busy.
|
||||
*/
|
||||
|
@ -1022,7 +1022,7 @@ static int omap_dev_ready(struct nand_chip *chip)
|
|||
|
||||
/**
|
||||
* omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip object
|
||||
* @mode: Read/Write mode
|
||||
*
|
||||
* When using BCH with SW correction (i.e. no ELM), sector size is set
|
||||
|
@ -1131,7 +1131,7 @@ static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
|
|||
* _omap_calculate_ecc_bch - Generate ECC bytes for one sector
|
||||
* @mtd: MTD device structure
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
* @ecc_calc: The ecc_code buffer
|
||||
* @i: The sector number (for a multi sector page)
|
||||
*
|
||||
* Support calculating of BCH4/8/16 ECC vectors for one sector
|
||||
|
@ -1259,7 +1259,7 @@ static int _omap_calculate_ecc_bch(struct mtd_info *mtd,
|
|||
* omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction
|
||||
* @chip: NAND chip object
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
* @ecc_calc: Buffer storing the calculated ECC bytes
|
||||
*
|
||||
* Support calculating of BCH4/8/16 ECC vectors for one sector. This is used
|
||||
* when SW based correction is required as ECC is required for one sector
|
||||
|
@ -1275,7 +1275,7 @@ static int omap_calculate_ecc_bch_sw(struct nand_chip *chip,
|
|||
* omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors
|
||||
* @mtd: MTD device structure
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
* @ecc_calc: Buffer storing the calculated ECC bytes
|
||||
*
|
||||
* Support calculating of BCH4/8/16 ecc vectors for the entire page in one go.
|
||||
*/
|
||||
|
@ -1674,7 +1674,8 @@ static int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf,
|
|||
|
||||
/**
|
||||
* is_elm_present - checks for presence of ELM module by scanning DT nodes
|
||||
* @omap_nand_info: NAND device structure containing platform data
|
||||
* @info: NAND device structure containing platform data
|
||||
* @elm_node: ELM's DT node
|
||||
*/
|
||||
static bool is_elm_present(struct omap_nand_info *info,
|
||||
struct device_node *elm_node)
|
||||
|
@ -2041,16 +2042,16 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
|||
chip->ecc.bytes = 7;
|
||||
chip->ecc.strength = 4;
|
||||
chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
chip->ecc.correct = nand_bch_correct_data;
|
||||
chip->ecc.correct = rawnand_sw_bch_correct;
|
||||
chip->ecc.calculate = omap_calculate_ecc_bch_sw;
|
||||
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
|
||||
/* Reserve one byte for the OMAP marker */
|
||||
oobbytes_per_step = chip->ecc.bytes + 1;
|
||||
/* Software BCH library is used for locating errors */
|
||||
chip->ecc.priv = nand_bch_init(mtd);
|
||||
if (!chip->ecc.priv) {
|
||||
err = rawnand_sw_bch_init(chip);
|
||||
if (err) {
|
||||
dev_err(dev, "Unable to use BCH library\n");
|
||||
return -EINVAL;
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2083,16 +2084,16 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
|||
chip->ecc.bytes = 13;
|
||||
chip->ecc.strength = 8;
|
||||
chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
chip->ecc.correct = nand_bch_correct_data;
|
||||
chip->ecc.correct = rawnand_sw_bch_correct;
|
||||
chip->ecc.calculate = omap_calculate_ecc_bch_sw;
|
||||
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
|
||||
/* Reserve one byte for the OMAP marker */
|
||||
oobbytes_per_step = chip->ecc.bytes + 1;
|
||||
/* Software BCH library is used for locating errors */
|
||||
chip->ecc.priv = nand_bch_init(mtd);
|
||||
if (!chip->ecc.priv) {
|
||||
err = rawnand_sw_bch_init(chip);
|
||||
if (err) {
|
||||
dev_err(dev, "unable to use BCH library\n");
|
||||
return -EINVAL;
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2195,7 +2196,6 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
nand_chip = &info->nand;
|
||||
mtd = nand_to_mtd(nand_chip);
|
||||
mtd->dev.parent = &pdev->dev;
|
||||
nand_chip->ecc.priv = NULL;
|
||||
nand_set_flash_node(nand_chip, dev->of_node);
|
||||
|
||||
if (!mtd->name) {
|
||||
|
@ -2271,10 +2271,9 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
return_error:
|
||||
if (!IS_ERR_OR_NULL(info->dma))
|
||||
dma_release_channel(info->dma);
|
||||
if (nand_chip->ecc.priv) {
|
||||
nand_bch_free(nand_chip->ecc.priv);
|
||||
nand_chip->ecc.priv = NULL;
|
||||
}
|
||||
|
||||
rawnand_sw_bch_cleanup(nand_chip);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -2285,10 +2284,8 @@ static int omap_nand_remove(struct platform_device *pdev)
|
|||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
int ret;
|
||||
|
||||
if (nand_chip->ecc.priv) {
|
||||
nand_bch_free(nand_chip->ecc.priv);
|
||||
nand_chip->ecc.priv = NULL;
|
||||
}
|
||||
rawnand_sw_bch_cleanup(nand_chip);
|
||||
|
||||
if (info->dma)
|
||||
dma_release_channel(info->dma);
|
||||
ret = mtd_device_unregister(mtd);
|
||||
|
|
|
@ -96,6 +96,9 @@ static u32 elm_read_reg(struct elm_info *info, int offset)
|
|||
* elm_config - Configure ELM module
|
||||
* @dev: ELM device
|
||||
* @bch_type: Type of BCH ecc
|
||||
* @ecc_steps: ECC steps to assign to config
|
||||
* @ecc_step_size: ECC step size to assign to config
|
||||
* @ecc_syndrome_size: ECC syndrome size to assign to config
|
||||
*/
|
||||
int elm_config(struct device *dev, enum bch_ecc bch_type,
|
||||
int ecc_steps, int ecc_step_size, int ecc_syndrome_size)
|
||||
|
@ -432,7 +435,7 @@ static int elm_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**
|
||||
/*
|
||||
* elm_context_save
|
||||
* saves ELM configurations to preserve them across Hardware powered-down
|
||||
*/
|
||||
|
@ -480,7 +483,7 @@ static int elm_context_save(struct elm_info *info)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* elm_context_restore
|
||||
* writes configurations saved duing power-down back into ELM registers
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
|
|
@ -145,6 +145,7 @@
|
|||
#define OP_PAGE_READ 0x2
|
||||
#define OP_PAGE_READ_WITH_ECC 0x3
|
||||
#define OP_PAGE_READ_WITH_ECC_SPARE 0x4
|
||||
#define OP_PAGE_READ_ONFI_READ 0x5
|
||||
#define OP_PROGRAM_PAGE 0x6
|
||||
#define OP_PAGE_PROGRAM_WITH_ECC 0x7
|
||||
#define OP_PROGRAM_PAGE_SPARE 0x9
|
||||
|
@ -460,12 +461,14 @@ struct qcom_nand_host {
|
|||
* @ecc_modes - ecc mode for NAND
|
||||
* @is_bam - whether NAND controller is using BAM
|
||||
* @is_qpic - whether NAND CTRL is part of qpic IP
|
||||
* @qpic_v2 - flag to indicate QPIC IP version 2
|
||||
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
|
||||
*/
|
||||
struct qcom_nandc_props {
|
||||
u32 ecc_modes;
|
||||
bool is_bam;
|
||||
bool is_qpic;
|
||||
bool qpic_v2;
|
||||
u32 dev_cmd_reg_start;
|
||||
};
|
||||
|
||||
|
@ -1164,7 +1167,13 @@ static int nandc_param(struct qcom_nand_host *host)
|
|||
* in use. we configure the controller to perform a raw read of 512
|
||||
* bytes to read onfi params
|
||||
*/
|
||||
nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ | PAGE_ACC | LAST_PAGE);
|
||||
if (nandc->props->qpic_v2)
|
||||
nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ_ONFI_READ |
|
||||
PAGE_ACC | LAST_PAGE);
|
||||
else
|
||||
nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ |
|
||||
PAGE_ACC | LAST_PAGE);
|
||||
|
||||
nandc_set_reg(nandc, NAND_ADDR0, 0);
|
||||
nandc_set_reg(nandc, NAND_ADDR1, 0);
|
||||
nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
|
||||
|
@ -1180,21 +1189,28 @@ static int nandc_param(struct qcom_nand_host *host)
|
|||
| 1 << DEV0_CFG1_ECC_DISABLE);
|
||||
nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
|
||||
|
||||
/* configure CMD1 and VLD for ONFI param probing */
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
|
||||
(nandc->vld & ~READ_START_VLD));
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD1,
|
||||
(nandc->cmd1 & ~(0xFF << READ_ADDR))
|
||||
| NAND_CMD_PARAM << READ_ADDR);
|
||||
/* configure CMD1 and VLD for ONFI param probing in QPIC v1 */
|
||||
if (!nandc->props->qpic_v2) {
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
|
||||
(nandc->vld & ~READ_START_VLD));
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD1,
|
||||
(nandc->cmd1 & ~(0xFF << READ_ADDR))
|
||||
| NAND_CMD_PARAM << READ_ADDR);
|
||||
}
|
||||
|
||||
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
|
||||
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
|
||||
if (!nandc->props->qpic_v2) {
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
|
||||
nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
|
||||
}
|
||||
|
||||
nandc_set_read_loc(nandc, 0, 0, 512, 1);
|
||||
|
||||
write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
|
||||
write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
|
||||
if (!nandc->props->qpic_v2) {
|
||||
write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
|
||||
write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
|
||||
}
|
||||
|
||||
nandc->buf_count = 512;
|
||||
memset(nandc->data_buffer, 0xff, nandc->buf_count);
|
||||
|
@ -1205,8 +1221,10 @@ static int nandc_param(struct qcom_nand_host *host)
|
|||
nandc->buf_count, 0);
|
||||
|
||||
/* restore CMD1 and VLD regs */
|
||||
write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0);
|
||||
write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL);
|
||||
if (!nandc->props->qpic_v2) {
|
||||
write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0);
|
||||
write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1570,6 +1588,8 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt)
|
|||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
int i;
|
||||
|
||||
nandc_read_buffer_sync(nandc, true);
|
||||
|
||||
for (i = 0; i < cw_cnt; i++) {
|
||||
u32 flash = le32_to_cpu(nandc->reg_read_buf[i]);
|
||||
|
||||
|
@ -2770,8 +2790,10 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
|
|||
/* kill onenand */
|
||||
if (!nandc->props->is_qpic)
|
||||
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
|
||||
nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
|
||||
NAND_DEV_CMD_VLD_VAL);
|
||||
|
||||
if (!nandc->props->qpic_v2)
|
||||
nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
|
||||
NAND_DEV_CMD_VLD_VAL);
|
||||
|
||||
/* enable ADM or BAM DMA */
|
||||
if (nandc->props->is_bam) {
|
||||
|
@ -2791,8 +2813,10 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
|
|||
}
|
||||
|
||||
/* save the original values of these registers */
|
||||
nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
|
||||
nandc->vld = NAND_DEV_CMD_VLD_VAL;
|
||||
if (!nandc->props->qpic_v2) {
|
||||
nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
|
||||
nandc->vld = NAND_DEV_CMD_VLD_VAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3050,6 +3074,14 @@ static const struct qcom_nandc_props ipq8074_nandc_props = {
|
|||
.dev_cmd_reg_start = 0x7000,
|
||||
};
|
||||
|
||||
static const struct qcom_nandc_props sdx55_nandc_props = {
|
||||
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
|
||||
.is_bam = true,
|
||||
.is_qpic = true,
|
||||
.qpic_v2 = true,
|
||||
.dev_cmd_reg_start = 0x7000,
|
||||
};
|
||||
|
||||
/*
|
||||
* data will hold a struct pointer containing more differences once we support
|
||||
* more controller variants
|
||||
|
@ -3063,10 +3095,18 @@ static const struct of_device_id qcom_nandc_of_match[] = {
|
|||
.compatible = "qcom,ipq4019-nand",
|
||||
.data = &ipq4019_nandc_props,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,ipq6018-nand",
|
||||
.data = &ipq8074_nandc_props,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,ipq8074-nand",
|
||||
.data = &ipq8074_nandc_props,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,sdx55-nand",
|
||||
.data = &sdx55_nandc_props,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <linux/platform_data/mtd-nand-s3c2410.h>
|
||||
|
@ -134,7 +133,8 @@ enum s3c_nand_clk_state {
|
|||
|
||||
/**
|
||||
* struct s3c2410_nand_info - NAND controller state.
|
||||
* @mtds: An array of MTD instances on this controoler.
|
||||
* @controller: Base controller structure.
|
||||
* @mtds: An array of MTD instances on this controller.
|
||||
* @platform: The platform data for this board.
|
||||
* @device: The platform device we bound to.
|
||||
* @clk: The clock resource for this controller.
|
||||
|
@ -146,6 +146,7 @@ enum s3c_nand_clk_state {
|
|||
* @clk_rate: The clock rate from @clk.
|
||||
* @clk_state: The current clock state.
|
||||
* @cpu_type: The exact type of this controller.
|
||||
* @freq_transition: CPUFreq notifier block
|
||||
*/
|
||||
struct s3c2410_nand_info {
|
||||
/* mtd info */
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/sharpsl.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -107,7 +106,7 @@ static int sharpsl_attach_chip(struct nand_chip *chip)
|
|||
chip->ecc.strength = 1;
|
||||
chip->ecc.hwctl = sharpsl_nand_enable_hwecc;
|
||||
chip->ecc.calculate = sharpsl_nand_calculate_ecc;
|
||||
chip->ecc.correct = nand_correct_data;
|
||||
chip->ecc.correct = rawnand_sw_hamming_correct;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
|
||||
#define NFC_REG_SPARE_AREA 0x00A0
|
||||
#define NFC_REG_PAT_ID 0x00A4
|
||||
#define NFC_REG_MDMA_ADDR 0x00C0
|
||||
#define NFC_REG_MDMA_CNT 0x00C4
|
||||
#define NFC_RAM0_BASE 0x0400
|
||||
#define NFC_RAM1_BASE 0x0800
|
||||
|
@ -182,6 +183,7 @@ struct sunxi_nand_hw_ecc {
|
|||
*
|
||||
* @node: used to store NAND chips into a list
|
||||
* @nand: base NAND chip structure
|
||||
* @ecc: ECC controller structure
|
||||
* @clk_rate: clk_rate required for this NAND chip
|
||||
* @timing_cfg: TIMING_CFG register value for this NAND chip
|
||||
* @timing_ctl: TIMING_CTL register value for this NAND chip
|
||||
|
@ -191,6 +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;
|
||||
unsigned long clk_rate;
|
||||
u32 timing_cfg;
|
||||
u32 timing_ctl;
|
||||
|
@ -207,13 +210,13 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
|
|||
* NAND Controller capabilities structure: stores NAND controller capabilities
|
||||
* for distinction between compatible strings.
|
||||
*
|
||||
* @extra_mbus_conf: Contrary to A10, A10s and A13, accessing internal RAM
|
||||
* @has_mdma: Use mbus dma mode, otherwise general dma
|
||||
* through MBUS on A23/A33 needs extra configuration.
|
||||
* @reg_io_data: I/O data register
|
||||
* @dma_maxburst: DMA maxburst
|
||||
*/
|
||||
struct sunxi_nfc_caps {
|
||||
bool extra_mbus_conf;
|
||||
bool has_mdma;
|
||||
unsigned int reg_io_data;
|
||||
unsigned int dma_maxburst;
|
||||
};
|
||||
|
@ -233,6 +236,7 @@ struct sunxi_nfc_caps {
|
|||
* controller
|
||||
* @complete: a completion object used to wait for NAND controller events
|
||||
* @dmac: the DMA channel attached to the NAND controller
|
||||
* @caps: NAND Controller capabilities
|
||||
*/
|
||||
struct sunxi_nfc {
|
||||
struct nand_controller controller;
|
||||
|
@ -363,24 +367,31 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
|
|||
if (!ret)
|
||||
return -ENOMEM;
|
||||
|
||||
dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
|
||||
if (!dmad) {
|
||||
ret = -EINVAL;
|
||||
goto err_unmap_buf;
|
||||
if (!nfc->caps->has_mdma) {
|
||||
dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
|
||||
if (!dmad) {
|
||||
ret = -EINVAL;
|
||||
goto err_unmap_buf;
|
||||
}
|
||||
}
|
||||
|
||||
writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
|
||||
nfc->regs + NFC_REG_CTL);
|
||||
writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
|
||||
writel(chunksize, nfc->regs + NFC_REG_CNT);
|
||||
if (nfc->caps->extra_mbus_conf)
|
||||
|
||||
if (nfc->caps->has_mdma) {
|
||||
writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
|
||||
nfc->regs + NFC_REG_CTL);
|
||||
writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
|
||||
writel(sg_dma_address(sg), nfc->regs + NFC_REG_MDMA_ADDR);
|
||||
} else {
|
||||
dmat = dmaengine_submit(dmad);
|
||||
|
||||
dmat = dmaengine_submit(dmad);
|
||||
|
||||
ret = dma_submit_error(dmat);
|
||||
if (ret)
|
||||
goto err_clr_dma_flag;
|
||||
ret = dma_submit_error(dmat);
|
||||
if (ret)
|
||||
goto err_clr_dma_flag;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -676,15 +687,15 @@ static void sunxi_nfc_randomizer_read_buf(struct nand_chip *nand, uint8_t *buf,
|
|||
|
||||
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);
|
||||
struct sunxi_nand_hw_ecc *data = nand->ecc.priv;
|
||||
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(data->mode) | NFC_ECC_EXCEPTION |
|
||||
NFC_ECC_PIPELINE;
|
||||
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;
|
||||
|
@ -911,7 +922,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
|
|||
unsigned int max_bitflips = 0;
|
||||
int ret, i, raw_mode = 0;
|
||||
struct scatterlist sg;
|
||||
u32 status;
|
||||
u32 status, wait;
|
||||
|
||||
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
|
||||
if (ret)
|
||||
|
@ -929,13 +940,18 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
|
|||
writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
|
||||
NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
|
||||
|
||||
dma_async_issue_pending(nfc->dmac);
|
||||
wait = NFC_CMD_INT_FLAG;
|
||||
|
||||
if (nfc->caps->has_mdma)
|
||||
wait |= NFC_DMA_INT_FLAG;
|
||||
else
|
||||
dma_async_issue_pending(nfc->dmac);
|
||||
|
||||
writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
|
||||
nfc->regs + NFC_REG_CMD);
|
||||
|
||||
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
|
||||
if (ret)
|
||||
ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
|
||||
if (ret && !nfc->caps->has_mdma)
|
||||
dmaengine_terminate_all(nfc->dmac);
|
||||
|
||||
sunxi_nfc_randomizer_disable(nand);
|
||||
|
@ -1276,6 +1292,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
|
|||
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||
struct scatterlist sg;
|
||||
u32 wait;
|
||||
int ret, i;
|
||||
|
||||
sunxi_nfc_select_chip(nand, nand->cur_cs);
|
||||
|
@ -1304,14 +1321,19 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
|
|||
writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
|
||||
nfc->regs + NFC_REG_WCMD_SET);
|
||||
|
||||
dma_async_issue_pending(nfc->dmac);
|
||||
wait = NFC_CMD_INT_FLAG;
|
||||
|
||||
if (nfc->caps->has_mdma)
|
||||
wait |= NFC_DMA_INT_FLAG;
|
||||
else
|
||||
dma_async_issue_pending(nfc->dmac);
|
||||
|
||||
writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
|
||||
NFC_DATA_TRANS | NFC_ACCESS_DIR,
|
||||
nfc->regs + NFC_REG_CMD);
|
||||
|
||||
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
|
||||
if (ret)
|
||||
ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
|
||||
if (ret && !nfc->caps->has_mdma)
|
||||
dmaengine_terminate_all(nfc->dmac);
|
||||
|
||||
sunxi_nfc_randomizer_disable(nand);
|
||||
|
@ -1597,9 +1619,9 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
|
|||
.free = sunxi_nand_ooblayout_free,
|
||||
};
|
||||
|
||||
static void sunxi_nand_hw_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
|
||||
static void sunxi_nand_hw_ecc_ctrl_cleanup(struct sunxi_nand_chip *sunxi_nand)
|
||||
{
|
||||
kfree(ecc->priv);
|
||||
kfree(sunxi_nand->ecc);
|
||||
}
|
||||
|
||||
static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
||||
|
@ -1607,10 +1629,10 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
struct device_node *np)
|
||||
{
|
||||
static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
|
||||
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
|
||||
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
struct nand_device *nanddev = mtd_to_nanddev(mtd);
|
||||
struct sunxi_nand_hw_ecc *data;
|
||||
int nsectors;
|
||||
int ret;
|
||||
int i;
|
||||
|
@ -1647,8 +1669,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
if (ecc->size != 512 && ecc->size != 1024)
|
||||
return -EINVAL;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
sunxi_nand->ecc = kzalloc(sizeof(*sunxi_nand->ecc), GFP_KERNEL);
|
||||
if (!sunxi_nand->ecc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Prefer 1k ECC chunk over 512 ones */
|
||||
|
@ -1675,7 +1697,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
goto err;
|
||||
}
|
||||
|
||||
data->mode = i;
|
||||
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);
|
||||
|
@ -1693,9 +1715,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
|
||||
ecc->write_oob = sunxi_nfc_hw_ecc_write_oob;
|
||||
mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
|
||||
ecc->priv = data;
|
||||
|
||||
if (nfc->dmac) {
|
||||
if (nfc->dmac || nfc->caps->has_mdma) {
|
||||
ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
|
||||
ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
|
||||
ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
|
||||
|
@ -1714,16 +1735,18 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
return 0;
|
||||
|
||||
err:
|
||||
kfree(data);
|
||||
kfree(sunxi_nand->ecc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
|
||||
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(ecc);
|
||||
sunxi_nand_hw_ecc_ctrl_cleanup(sunxi_nand);
|
||||
break;
|
||||
case NAND_ECC_ENGINE_TYPE_NONE:
|
||||
default:
|
||||
|
@ -2053,11 +2076,41 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
|
|||
ret = mtd_device_unregister(nand_to_mtd(chip));
|
||||
WARN_ON(ret);
|
||||
nand_cleanup(chip);
|
||||
sunxi_nand_ecc_cleanup(&chip->ecc);
|
||||
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;
|
||||
|
||||
if (nfc->caps->has_mdma)
|
||||
return 0;
|
||||
|
||||
nfc->dmac = dma_request_chan(nfc->dev, "rxtx");
|
||||
if (IS_ERR(nfc->dmac)) {
|
||||
ret = PTR_ERR(nfc->dmac);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
/* Ignore errors to fall back to PIO mode */
|
||||
dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret);
|
||||
nfc->dmac = NULL;
|
||||
} else {
|
||||
struct dma_slave_config dmac_cfg = { };
|
||||
|
||||
dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
|
||||
dmac_cfg.dst_addr = dmac_cfg.src_addr;
|
||||
dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
|
||||
dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
|
||||
dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
|
||||
dmaengine_slave_config(nfc->dmac, &dmac_cfg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_nfc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -2132,30 +2185,10 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto out_ahb_reset_reassert;
|
||||
|
||||
nfc->dmac = dma_request_chan(dev, "rxtx");
|
||||
if (IS_ERR(nfc->dmac)) {
|
||||
ret = PTR_ERR(nfc->dmac);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto out_ahb_reset_reassert;
|
||||
ret = sunxi_nfc_dma_init(nfc, r);
|
||||
|
||||
/* Ignore errors to fall back to PIO mode */
|
||||
dev_warn(dev, "failed to request rxtx DMA channel: %d\n", ret);
|
||||
nfc->dmac = NULL;
|
||||
} else {
|
||||
struct dma_slave_config dmac_cfg = { };
|
||||
|
||||
dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
|
||||
dmac_cfg.dst_addr = dmac_cfg.src_addr;
|
||||
dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
|
||||
dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
|
||||
dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
|
||||
dmaengine_slave_config(nfc->dmac, &dmac_cfg);
|
||||
|
||||
if (nfc->caps->extra_mbus_conf)
|
||||
writel(readl(nfc->regs + NFC_REG_CTL) |
|
||||
NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL);
|
||||
}
|
||||
if (ret)
|
||||
goto out_ahb_reset_reassert;
|
||||
|
||||
platform_set_drvdata(pdev, nfc);
|
||||
|
||||
|
@ -2202,7 +2235,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
|
|||
};
|
||||
|
||||
static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
|
||||
.extra_mbus_conf = true,
|
||||
.has_mdma = true,
|
||||
.reg_io_data = NFC_REG_A23_IO_DATA,
|
||||
.dma_maxburst = 8,
|
||||
};
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -293,11 +292,11 @@ static int tmio_nand_correct_data(struct nand_chip *chip, unsigned char *buf,
|
|||
int r0, r1;
|
||||
|
||||
/* assume ecc.size = 512 and ecc.bytes = 6 */
|
||||
r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256, false);
|
||||
r0 = rawnand_sw_hamming_correct(chip, buf, read_ecc, calc_ecc);
|
||||
if (r0 < 0)
|
||||
return r0;
|
||||
r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256,
|
||||
false);
|
||||
r1 = rawnand_sw_hamming_correct(chip, buf + 256, read_ecc + 3,
|
||||
calc_ecc + 3);
|
||||
if (r1 < 0)
|
||||
return r1;
|
||||
return r0 + r1;
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_data/txx9/ndfmc.h>
|
||||
|
@ -194,8 +193,8 @@ static int txx9ndfmc_correct_data(struct nand_chip *chip, unsigned char *buf,
|
|||
int stat;
|
||||
|
||||
for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) {
|
||||
stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256,
|
||||
false);
|
||||
stat = rawnand_sw_hamming_correct(chip, buf, read_ecc,
|
||||
calc_ecc);
|
||||
if (stat < 0)
|
||||
return stat;
|
||||
corrected += stat;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
menuconfig MTD_SPI_NAND
|
||||
tristate "SPI NAND device Support"
|
||||
select MTD_NAND_CORE
|
||||
select MTD_NAND_ECC
|
||||
depends on SPI_MASTER
|
||||
select SPI_MEM
|
||||
help
|
||||
|
|
|
@ -193,6 +193,135 @@ static int spinand_ecc_enable(struct spinand_device *spinand,
|
|||
enable ? CFG_ECC_ENABLE : 0);
|
||||
}
|
||||
|
||||
static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
|
||||
if (spinand->eccinfo.get_status)
|
||||
return spinand->eccinfo.get_status(spinand, status);
|
||||
|
||||
switch (status & STATUS_ECC_MASK) {
|
||||
case STATUS_ECC_NO_BITFLIPS:
|
||||
return 0;
|
||||
|
||||
case STATUS_ECC_HAS_BITFLIPS:
|
||||
/*
|
||||
* We have no way to know exactly how many bitflips have been
|
||||
* fixed, so let's return the maximum possible value so that
|
||||
* wear-leveling layers move the data immediately.
|
||||
*/
|
||||
return nanddev_get_ecc_conf(nand)->strength;
|
||||
|
||||
case STATUS_ECC_UNCOR_ERROR:
|
||||
return -EBADMSG;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
/* Reserve 2 bytes for the BBM. */
|
||||
region->offset = 2;
|
||||
region->length = 62;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = {
|
||||
.ecc = spinand_noecc_ooblayout_ecc,
|
||||
.free = spinand_noecc_ooblayout_free,
|
||||
};
|
||||
|
||||
static int spinand_ondie_ecc_init_ctx(struct nand_device *nand)
|
||||
{
|
||||
struct spinand_device *spinand = nand_to_spinand(nand);
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
struct spinand_ondie_ecc_conf *engine_conf;
|
||||
|
||||
nand->ecc.ctx.conf.engine_type = NAND_ECC_ENGINE_TYPE_ON_DIE;
|
||||
nand->ecc.ctx.conf.step_size = nand->ecc.requirements.step_size;
|
||||
nand->ecc.ctx.conf.strength = nand->ecc.requirements.strength;
|
||||
|
||||
engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
|
||||
if (!engine_conf)
|
||||
return -ENOMEM;
|
||||
|
||||
nand->ecc.ctx.priv = engine_conf;
|
||||
|
||||
if (spinand->eccinfo.ooblayout)
|
||||
mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout);
|
||||
else
|
||||
mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spinand_ondie_ecc_cleanup_ctx(struct nand_device *nand)
|
||||
{
|
||||
kfree(nand->ecc.ctx.priv);
|
||||
}
|
||||
|
||||
static int spinand_ondie_ecc_prepare_io_req(struct nand_device *nand,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct spinand_device *spinand = nand_to_spinand(nand);
|
||||
bool enable = (req->mode != MTD_OPS_RAW);
|
||||
|
||||
/* Only enable or disable the engine */
|
||||
return spinand_ecc_enable(spinand, enable);
|
||||
}
|
||||
|
||||
static int spinand_ondie_ecc_finish_io_req(struct nand_device *nand,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
struct spinand_device *spinand = nand_to_spinand(nand);
|
||||
|
||||
if (req->mode == MTD_OPS_RAW)
|
||||
return 0;
|
||||
|
||||
/* Nothing to do when finishing a page write */
|
||||
if (req->type == NAND_PAGE_WRITE)
|
||||
return 0;
|
||||
|
||||
/* Finish a page write: check the status, report errors/bitflips */
|
||||
return spinand_check_ecc_status(spinand, engine_conf->status);
|
||||
}
|
||||
|
||||
static struct nand_ecc_engine_ops spinand_ondie_ecc_engine_ops = {
|
||||
.init_ctx = spinand_ondie_ecc_init_ctx,
|
||||
.cleanup_ctx = spinand_ondie_ecc_cleanup_ctx,
|
||||
.prepare_io_req = spinand_ondie_ecc_prepare_io_req,
|
||||
.finish_io_req = spinand_ondie_ecc_finish_io_req,
|
||||
};
|
||||
|
||||
static struct nand_ecc_engine spinand_ondie_ecc_engine = {
|
||||
.ops = &spinand_ondie_ecc_engine_ops,
|
||||
};
|
||||
|
||||
static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status)
|
||||
{
|
||||
struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
|
||||
if (nand->ecc.ctx.conf.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE &&
|
||||
engine_conf)
|
||||
engine_conf->status = status;
|
||||
}
|
||||
|
||||
static int spinand_write_enable_op(struct spinand_device *spinand)
|
||||
{
|
||||
struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true);
|
||||
|
@ -214,7 +343,6 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
|
|||
const struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
struct spi_mem_dirmap_desc *rdesc;
|
||||
unsigned int nbytes = 0;
|
||||
void *buf = NULL;
|
||||
|
@ -254,16 +382,9 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
|
|||
memcpy(req->databuf.in, spinand->databuf + req->dataoffs,
|
||||
req->datalen);
|
||||
|
||||
if (req->ooblen) {
|
||||
if (req->mode == MTD_OPS_AUTO_OOB)
|
||||
mtd_ooblayout_get_databytes(mtd, req->oobbuf.in,
|
||||
spinand->oobbuf,
|
||||
req->ooboffs,
|
||||
req->ooblen);
|
||||
else
|
||||
memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs,
|
||||
req->ooblen);
|
||||
}
|
||||
if (req->ooblen)
|
||||
memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs,
|
||||
req->ooblen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -272,7 +393,7 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
|
|||
const struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
struct mtd_info *mtd = spinand_to_mtd(spinand);
|
||||
struct spi_mem_dirmap_desc *wdesc;
|
||||
unsigned int nbytes, column = 0;
|
||||
void *buf = spinand->databuf;
|
||||
|
@ -284,9 +405,12 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
|
|||
* must fill the page cache entirely even if we only want to program
|
||||
* the data portion of the page, otherwise we might corrupt the BBM or
|
||||
* user data previously programmed in OOB area.
|
||||
*
|
||||
* Only reset the data buffer manually, the OOB buffer is prepared by
|
||||
* ECC engines ->prepare_io_req() callback.
|
||||
*/
|
||||
nbytes = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
|
||||
memset(spinand->databuf, 0xff, nbytes);
|
||||
memset(spinand->databuf, 0xff, nanddev_page_size(nand));
|
||||
|
||||
if (req->datalen)
|
||||
memcpy(spinand->databuf + req->dataoffs, req->databuf.out,
|
||||
|
@ -402,42 +526,17 @@ static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
|
|||
return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock);
|
||||
}
|
||||
|
||||
static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status)
|
||||
static int spinand_read_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
|
||||
if (spinand->eccinfo.get_status)
|
||||
return spinand->eccinfo.get_status(spinand, status);
|
||||
|
||||
switch (status & STATUS_ECC_MASK) {
|
||||
case STATUS_ECC_NO_BITFLIPS:
|
||||
return 0;
|
||||
|
||||
case STATUS_ECC_HAS_BITFLIPS:
|
||||
/*
|
||||
* We have no way to know exactly how many bitflips have been
|
||||
* fixed, so let's return the maximum possible value so that
|
||||
* wear-leveling layers move the data immediately.
|
||||
*/
|
||||
return nanddev_get_ecc_conf(nand)->strength;
|
||||
|
||||
case STATUS_ECC_UNCOR_ERROR:
|
||||
return -EBADMSG;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int spinand_read_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req,
|
||||
bool ecc_enabled)
|
||||
{
|
||||
u8 status;
|
||||
int ret;
|
||||
|
||||
ret = nand_ecc_prepare_io_req(nand, (struct nand_page_io_req *)req);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spinand_load_page_op(spinand, req);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -446,22 +545,26 @@ static int spinand_read_page(struct spinand_device *spinand,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
spinand_ondie_ecc_save_status(nand, status);
|
||||
|
||||
ret = spinand_read_from_cache_op(spinand, req);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!ecc_enabled)
|
||||
return 0;
|
||||
|
||||
return spinand_check_ecc_status(spinand, status);
|
||||
return nand_ecc_finish_io_req(nand, (struct nand_page_io_req *)req);
|
||||
}
|
||||
|
||||
static int spinand_write_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
u8 status;
|
||||
int ret;
|
||||
|
||||
ret = nand_ecc_prepare_io_req(nand, (struct nand_page_io_req *)req);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spinand_write_enable_op(spinand);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -476,9 +579,9 @@ static int spinand_write_page(struct spinand_device *spinand,
|
|||
|
||||
ret = spinand_wait(spinand, &status);
|
||||
if (!ret && (status & STATUS_PROG_FAILED))
|
||||
ret = -EIO;
|
||||
return -EIO;
|
||||
|
||||
return ret;
|
||||
return nand_ecc_finish_io_req(nand, (struct nand_page_io_req *)req);
|
||||
}
|
||||
|
||||
static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
|
||||
|
@ -488,25 +591,24 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
|
|||
struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
unsigned int max_bitflips = 0;
|
||||
struct nand_io_iter iter;
|
||||
bool enable_ecc = false;
|
||||
bool disable_ecc = false;
|
||||
bool ecc_failed = false;
|
||||
int ret = 0;
|
||||
|
||||
if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout)
|
||||
enable_ecc = true;
|
||||
if (ops->mode == MTD_OPS_RAW || !spinand->eccinfo.ooblayout)
|
||||
disable_ecc = true;
|
||||
|
||||
mutex_lock(&spinand->lock);
|
||||
|
||||
nanddev_io_for_each_page(nand, NAND_PAGE_READ, from, ops, &iter) {
|
||||
if (disable_ecc)
|
||||
iter.req.mode = MTD_OPS_RAW;
|
||||
|
||||
ret = spinand_select_target(spinand, iter.req.pos.target);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spinand_ecc_enable(spinand, enable_ecc);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spinand_read_page(spinand, &iter.req, enable_ecc);
|
||||
ret = spinand_read_page(spinand, &iter.req);
|
||||
if (ret < 0 && ret != -EBADMSG)
|
||||
break;
|
||||
|
||||
|
@ -537,20 +639,19 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t to,
|
|||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
struct nand_io_iter iter;
|
||||
bool enable_ecc = false;
|
||||
bool disable_ecc = false;
|
||||
int ret = 0;
|
||||
|
||||
if (ops->mode != MTD_OPS_RAW && mtd->ooblayout)
|
||||
enable_ecc = true;
|
||||
if (ops->mode == MTD_OPS_RAW || !mtd->ooblayout)
|
||||
disable_ecc = true;
|
||||
|
||||
mutex_lock(&spinand->lock);
|
||||
|
||||
nanddev_io_for_each_page(nand, NAND_PAGE_WRITE, to, ops, &iter) {
|
||||
ret = spinand_select_target(spinand, iter.req.pos.target);
|
||||
if (ret)
|
||||
break;
|
||||
if (disable_ecc)
|
||||
iter.req.mode = MTD_OPS_RAW;
|
||||
|
||||
ret = spinand_ecc_enable(spinand, enable_ecc);
|
||||
ret = spinand_select_target(spinand, iter.req.pos.target);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
|
@ -580,7 +681,7 @@ static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos)
|
|||
};
|
||||
|
||||
spinand_select_target(spinand, pos->target);
|
||||
spinand_read_page(spinand, &req, false);
|
||||
spinand_read_page(spinand, &req);
|
||||
if (marker[0] != 0xff || marker[1] != 0xff)
|
||||
return true;
|
||||
|
||||
|
@ -965,30 +1066,6 @@ static int spinand_detect(struct spinand_device *spinand)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
/* Reserve 2 bytes for the BBM. */
|
||||
region->offset = 2;
|
||||
region->length = 62;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = {
|
||||
.ecc = spinand_noecc_ooblayout_ecc,
|
||||
.free = spinand_noecc_ooblayout_free,
|
||||
};
|
||||
|
||||
static int spinand_init(struct spinand_device *spinand)
|
||||
{
|
||||
struct device *dev = &spinand->spimem->spi->dev;
|
||||
|
@ -1066,10 +1143,15 @@ static int spinand_init(struct spinand_device *spinand)
|
|||
if (ret)
|
||||
goto err_manuf_cleanup;
|
||||
|
||||
/*
|
||||
* Right now, we don't support ECC, so let the whole oob
|
||||
* area is available for user.
|
||||
*/
|
||||
/* SPI-NAND default ECC engine is on-die */
|
||||
nand->ecc.defaults.engine_type = NAND_ECC_ENGINE_TYPE_ON_DIE;
|
||||
nand->ecc.ondie_engine = &spinand_ondie_ecc_engine;
|
||||
|
||||
spinand_ecc_enable(spinand, false);
|
||||
ret = nanddev_ecc_engine_init(nand);
|
||||
if (ret)
|
||||
goto err_cleanup_nanddev;
|
||||
|
||||
mtd->_read_oob = spinand_mtd_read;
|
||||
mtd->_write_oob = spinand_mtd_write;
|
||||
mtd->_block_isbad = spinand_mtd_block_isbad;
|
||||
|
@ -1078,14 +1160,11 @@ static int spinand_init(struct spinand_device *spinand)
|
|||
mtd->_erase = spinand_mtd_erase;
|
||||
mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
|
||||
|
||||
if (spinand->eccinfo.ooblayout)
|
||||
mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout);
|
||||
else
|
||||
mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout);
|
||||
|
||||
ret = mtd_ooblayout_count_freebytes(mtd);
|
||||
if (ret < 0)
|
||||
goto err_cleanup_nanddev;
|
||||
if (nand->ecc.engine) {
|
||||
ret = mtd_ooblayout_count_freebytes(mtd);
|
||||
if (ret < 0)
|
||||
goto err_cleanup_ecc_engine;
|
||||
}
|
||||
|
||||
mtd->oobavail = ret;
|
||||
|
||||
|
@ -1095,6 +1174,9 @@ static int spinand_init(struct spinand_device *spinand)
|
|||
|
||||
return 0;
|
||||
|
||||
err_cleanup_ecc_engine:
|
||||
nanddev_ecc_engine_cleanup(nand);
|
||||
|
||||
err_cleanup_nanddev:
|
||||
nanddev_cleanup(nand);
|
||||
|
||||
|
|
|
@ -119,6 +119,53 @@ static const struct spinand_info macronix_spinand_table[] = {
|
|||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
|
||||
SPINAND_INFO("MX35LF2GE4AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x26),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35LF4GE4AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37),
|
||||
NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35LF1G24AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
|
||||
SPINAND_INFO("MX35LF2G24AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
|
||||
SPINAND_INFO("MX35LF4G24AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
|
||||
SPINAND_INFO("MX31LF1GE4BC",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#define MICRON_SELECT_DIE(x) ((x) << 6)
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
static SPINAND_OP_VARIANTS(quadio_read_cache_variants,
|
||||
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
|
||||
|
@ -36,14 +36,27 @@ static SPINAND_OP_VARIANTS(read_cache_variants,
|
|||
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,
|
||||
static SPINAND_OP_VARIANTS(x4_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,
|
||||
static SPINAND_OP_VARIANTS(x4_update_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
|
||||
/* Micron MT29F2G01AAAED Device */
|
||||
static SPINAND_OP_VARIANTS(x4_read_cache_variants,
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X4_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(x1_write_cache_variants,
|
||||
SPINAND_PROG_LOAD(true, 0, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(x1_update_cache_variants,
|
||||
SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
|
||||
static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
|
@ -74,6 +87,47 @@ static const struct mtd_ooblayout_ops micron_8_ooblayout = {
|
|||
.free = micron_8_ooblayout_free,
|
||||
};
|
||||
|
||||
static int micron_4_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
|
||||
if (section >= spinand->base.memorg.pagesize /
|
||||
mtd->ecc_step_size)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = (section * 16) + 8;
|
||||
region->length = 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int micron_4_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
|
||||
if (section >= spinand->base.memorg.pagesize /
|
||||
mtd->ecc_step_size)
|
||||
return -ERANGE;
|
||||
|
||||
if (section) {
|
||||
region->offset = 16 * section;
|
||||
region->length = 8;
|
||||
} else {
|
||||
/* section 0 has two bytes reserved for the BBM */
|
||||
region->offset = 2;
|
||||
region->length = 6;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops micron_4_ooblayout = {
|
||||
.ecc = micron_4_ooblayout_ecc,
|
||||
.free = micron_4_ooblayout_free,
|
||||
};
|
||||
|
||||
static int micron_select_target(struct spinand_device *spinand,
|
||||
unsigned int target)
|
||||
{
|
||||
|
@ -120,9 +174,9 @@ static const struct spinand_info micron_spinand_table[] = {
|
|||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(µn_8_ooblayout,
|
||||
micron_8_ecc_get_status)),
|
||||
|
@ -131,9 +185,9 @@ static const struct spinand_info micron_spinand_table[] = {
|
|||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(µn_8_ooblayout,
|
||||
micron_8_ecc_get_status)),
|
||||
|
@ -142,9 +196,9 @@ static const struct spinand_info micron_spinand_table[] = {
|
|||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(µn_8_ooblayout,
|
||||
micron_8_ecc_get_status)),
|
||||
|
@ -153,9 +207,9 @@ static const struct spinand_info micron_spinand_table[] = {
|
|||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(µn_8_ooblayout,
|
||||
micron_8_ecc_get_status)),
|
||||
|
@ -164,9 +218,9 @@ static const struct spinand_info micron_spinand_table[] = {
|
|||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(µn_8_ooblayout,
|
||||
micron_8_ecc_get_status),
|
||||
|
@ -176,9 +230,9 @@ static const struct spinand_info micron_spinand_table[] = {
|
|||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34),
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
SPINAND_HAS_CR_FEAT_BIT,
|
||||
SPINAND_ECCINFO(µn_8_ooblayout,
|
||||
micron_8_ecc_get_status)),
|
||||
|
@ -187,9 +241,9 @@ static const struct spinand_info micron_spinand_table[] = {
|
|||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
SPINAND_HAS_CR_FEAT_BIT,
|
||||
SPINAND_ECCINFO(µn_8_ooblayout,
|
||||
micron_8_ecc_get_status)),
|
||||
|
@ -198,9 +252,9 @@ static const struct spinand_info micron_spinand_table[] = {
|
|||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46),
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
SPINAND_HAS_CR_FEAT_BIT,
|
||||
SPINAND_ECCINFO(µn_8_ooblayout,
|
||||
micron_8_ecc_get_status),
|
||||
|
@ -210,13 +264,23 @@ static const struct spinand_info micron_spinand_table[] = {
|
|||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47),
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
|
||||
&x4_write_cache_variants,
|
||||
&x4_update_cache_variants),
|
||||
SPINAND_HAS_CR_FEAT_BIT,
|
||||
SPINAND_ECCINFO(µn_8_ooblayout,
|
||||
micron_8_ecc_get_status),
|
||||
SPINAND_SELECT_TARGET(micron_select_target)),
|
||||
/* M69A 2Gb 3.3V */
|
||||
SPINAND_INFO("MT29F2G01AAAED",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9F),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 80, 2, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&x4_read_cache_variants,
|
||||
&x1_write_cache_variants,
|
||||
&x1_update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(µn_4_ooblayout, NULL)),
|
||||
};
|
||||
|
||||
static int micron_spinand_init(struct spinand_device *spinand)
|
||||
|
|
|
@ -28,7 +28,7 @@ static SPINAND_OP_VARIANTS(update_cache_x4_variants,
|
|||
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
|
||||
/**
|
||||
/*
|
||||
* Backward compatibility for 1st generation Serial NAND devices
|
||||
* which don't support Quad Program Load operation.
|
||||
*/
|
||||
|
|
|
@ -226,7 +226,7 @@ static int mtdpart_setup_real(char *s)
|
|||
struct cmdline_mtd_partition *this_mtd;
|
||||
struct mtd_partition *parts;
|
||||
int mtd_id_len, num_parts;
|
||||
char *p, *mtd_id, *semicol;
|
||||
char *p, *mtd_id, *semicol, *open_parenth;
|
||||
|
||||
/*
|
||||
* Replace the first ';' by a NULL char so strrchr can work
|
||||
|
@ -236,6 +236,14 @@ static int mtdpart_setup_real(char *s)
|
|||
if (semicol)
|
||||
*semicol = '\0';
|
||||
|
||||
/*
|
||||
* make sure that part-names with ":" will not be handled as
|
||||
* part of the mtd-id with an ":"
|
||||
*/
|
||||
open_parenth = strchr(s, '(');
|
||||
if (open_parenth)
|
||||
*open_parenth = '\0';
|
||||
|
||||
mtd_id = s;
|
||||
|
||||
/*
|
||||
|
@ -245,6 +253,10 @@ static int mtdpart_setup_real(char *s)
|
|||
*/
|
||||
p = strrchr(s, ':');
|
||||
|
||||
/* Restore the '(' now. */
|
||||
if (open_parenth)
|
||||
*open_parenth = '(';
|
||||
|
||||
/* Restore the ';' now. */
|
||||
if (semicol)
|
||||
*semicol = ';';
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <linux/sysfs.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
#include "nand/raw/sm_common.h"
|
||||
#include "sm_ftl.h"
|
||||
|
||||
|
@ -216,20 +216,19 @@ static void sm_break_offset(struct sm_ftl *ftl, loff_t loffset,
|
|||
|
||||
static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob)
|
||||
{
|
||||
bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
|
||||
uint8_t ecc[3];
|
||||
|
||||
__nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) < 0)
|
||||
ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order);
|
||||
if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc1, SM_SMALL_PAGE,
|
||||
sm_order) < 0)
|
||||
return -EIO;
|
||||
|
||||
buffer += SM_SMALL_PAGE;
|
||||
|
||||
__nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) < 0)
|
||||
ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order);
|
||||
if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc2, SM_SMALL_PAGE,
|
||||
sm_order) < 0)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
@ -369,6 +368,7 @@ static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf,
|
|||
int zone, int block, int lba,
|
||||
unsigned long invalid_bitmap)
|
||||
{
|
||||
bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
|
||||
struct sm_oob oob;
|
||||
int boffset;
|
||||
int retry = 0;
|
||||
|
@ -395,13 +395,13 @@ static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf,
|
|||
}
|
||||
|
||||
if (ftl->smallpagenand) {
|
||||
__nand_calculate_ecc(buf + boffset, SM_SMALL_PAGE,
|
||||
oob.ecc1,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
ecc_sw_hamming_calculate(buf + boffset,
|
||||
SM_SMALL_PAGE, oob.ecc1,
|
||||
sm_order);
|
||||
|
||||
__nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE,
|
||||
SM_SMALL_PAGE, oob.ecc2,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
ecc_sw_hamming_calculate(buf + boffset + SM_SMALL_PAGE,
|
||||
SM_SMALL_PAGE, oob.ecc2,
|
||||
sm_order);
|
||||
}
|
||||
if (!sm_write_sector(ftl, zone, block, boffset,
|
||||
buf + boffset, &oob))
|
||||
|
|
|
@ -24,6 +24,50 @@ config MTD_SPI_NOR_USE_4K_SECTORS
|
|||
Please note that some tools/drivers/filesystems may not work with
|
||||
4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
|
||||
|
||||
choice
|
||||
prompt "Software write protection at boot"
|
||||
default MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE
|
||||
|
||||
config MTD_SPI_NOR_SWP_DISABLE
|
||||
bool "Disable SWP on any flashes (legacy behavior)"
|
||||
help
|
||||
This option disables the software write protection on any SPI
|
||||
flashes at boot-up.
|
||||
|
||||
Depending on the flash chip this either clears the block protection
|
||||
bits or does a "Global Unprotect" command.
|
||||
|
||||
Don't use this if you intent to use the software write protection
|
||||
of your SPI flash. This is only to keep backwards compatibility.
|
||||
|
||||
config MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE
|
||||
bool "Disable SWP on flashes w/ volatile protection bits"
|
||||
help
|
||||
Some SPI flashes have volatile block protection bits, ie. after a
|
||||
power-up or a reset the flash is software write protected by
|
||||
default.
|
||||
|
||||
This option disables the software write protection for these kind
|
||||
of flashes while keeping it enabled for any other SPI flashes
|
||||
which have non-volatile write protection bits.
|
||||
|
||||
If the software write protection will be disabled depending on
|
||||
the flash either the block protection bits are cleared or a
|
||||
"Global Unprotect" command is issued.
|
||||
|
||||
If you are unsure, select this option.
|
||||
|
||||
config MTD_SPI_NOR_SWP_KEEP
|
||||
bool "Keep software write protection as is"
|
||||
help
|
||||
If you select this option the software write protection of any
|
||||
SPI flashes will not be changed. If your flash is software write
|
||||
protected or will be automatically software write protected after
|
||||
power-up you have to manually unlock it before you are able to
|
||||
write to it.
|
||||
|
||||
endchoice
|
||||
|
||||
source "drivers/mtd/spi-nor/controllers/Kconfig"
|
||||
|
||||
endif # MTD_SPI_NOR
|
||||
|
|
|
@ -8,39 +8,192 @@
|
|||
|
||||
#include "core.h"
|
||||
|
||||
#define ATMEL_SR_GLOBAL_PROTECT_MASK GENMASK(5, 2)
|
||||
|
||||
/*
|
||||
* The Atmel AT25FS010/AT25FS040 parts have some weird configuration for the
|
||||
* block protection bits. We don't support them. But legacy behavior in linux
|
||||
* is to unlock the whole flash array on startup. Therefore, we have to support
|
||||
* exactly this operation.
|
||||
*/
|
||||
static int atmel_at25fs_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int atmel_at25fs_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* We only support unlocking the whole flash array */
|
||||
if (ofs || len != nor->params->size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Write 0x00 to the status register to disable write protection */
|
||||
ret = spi_nor_write_sr_and_check(nor, 0);
|
||||
if (ret)
|
||||
dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_at25fs_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static const struct spi_nor_locking_ops atmel_at25fs_locking_ops = {
|
||||
.lock = atmel_at25fs_lock,
|
||||
.unlock = atmel_at25fs_unlock,
|
||||
.is_locked = atmel_at25fs_is_locked,
|
||||
};
|
||||
|
||||
static void atmel_at25fs_default_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->params->locking_ops = &atmel_at25fs_locking_ops;
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups atmel_at25fs_fixups = {
|
||||
.default_init = atmel_at25fs_default_init,
|
||||
};
|
||||
|
||||
/**
|
||||
* atmel_set_global_protection - Do a Global Protect or Unprotect command
|
||||
* @nor: pointer to 'struct spi_nor'
|
||||
* @ofs: offset in bytes
|
||||
* @len: len in bytes
|
||||
* @is_protect: if true do a Global Protect otherwise it is a Global Unprotect
|
||||
*
|
||||
* Return: 0 on success, -error otherwise.
|
||||
*/
|
||||
static int atmel_set_global_protection(struct spi_nor *nor, loff_t ofs,
|
||||
uint64_t len, bool is_protect)
|
||||
{
|
||||
int ret;
|
||||
u8 sr;
|
||||
|
||||
/* We only support locking the whole flash array */
|
||||
if (ofs || len != nor->params->size)
|
||||
return -EINVAL;
|
||||
|
||||
ret = spi_nor_read_sr(nor, nor->bouncebuf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sr = nor->bouncebuf[0];
|
||||
|
||||
/* SRWD bit needs to be cleared, otherwise the protection doesn't change */
|
||||
if (sr & SR_SRWD) {
|
||||
sr &= ~SR_SRWD;
|
||||
ret = spi_nor_write_sr_and_check(nor, sr);
|
||||
if (ret) {
|
||||
dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_protect) {
|
||||
sr |= ATMEL_SR_GLOBAL_PROTECT_MASK;
|
||||
/*
|
||||
* Set the SRWD bit again as soon as we are protecting
|
||||
* anything. This will ensure that the WP# pin is working
|
||||
* correctly. By doing this we also behave the same as
|
||||
* spi_nor_sr_lock(), which sets SRWD if any block protection
|
||||
* is active.
|
||||
*/
|
||||
sr |= SR_SRWD;
|
||||
} else {
|
||||
sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK;
|
||||
}
|
||||
|
||||
nor->bouncebuf[0] = sr;
|
||||
|
||||
/*
|
||||
* We cannot use the spi_nor_write_sr_and_check() because this command
|
||||
* isn't really setting any bits, instead it is an pseudo command for
|
||||
* "Global Unprotect" or "Global Protect"
|
||||
*/
|
||||
return spi_nor_write_sr(nor, nor->bouncebuf, 1);
|
||||
}
|
||||
|
||||
static int atmel_global_protect(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
{
|
||||
return atmel_set_global_protection(nor, ofs, len, true);
|
||||
}
|
||||
|
||||
static int atmel_global_unprotect(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
{
|
||||
return atmel_set_global_protection(nor, ofs, len, false);
|
||||
}
|
||||
|
||||
static int atmel_is_global_protected(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ofs >= nor->params->size || (ofs + len) > nor->params->size)
|
||||
return -EINVAL;
|
||||
|
||||
ret = spi_nor_read_sr(nor, nor->bouncebuf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK);
|
||||
}
|
||||
|
||||
static const struct spi_nor_locking_ops atmel_global_protection_ops = {
|
||||
.lock = atmel_global_protect,
|
||||
.unlock = atmel_global_unprotect,
|
||||
.is_locked = atmel_is_global_protected,
|
||||
};
|
||||
|
||||
static void atmel_global_protection_default_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->params->locking_ops = &atmel_global_protection_ops;
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups atmel_global_protection_fixups = {
|
||||
.default_init = atmel_global_protection_default_init,
|
||||
};
|
||||
|
||||
static const struct flash_info atmel_parts[] = {
|
||||
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
|
||||
{ "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) },
|
||||
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
|
||||
{ "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK)
|
||||
.fixups = &atmel_at25fs_fixups },
|
||||
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK)
|
||||
.fixups = &atmel_at25fs_fixups },
|
||||
|
||||
{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) },
|
||||
{ "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8,
|
||||
SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
|
||||
.fixups = &atmel_global_protection_fixups },
|
||||
{ "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64,
|
||||
SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
|
||||
.fixups = &atmel_global_protection_fixups },
|
||||
{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64,
|
||||
SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
|
||||
.fixups = &atmel_global_protection_fixups },
|
||||
{ "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128,
|
||||
SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
|
||||
.fixups = &atmel_global_protection_fixups },
|
||||
|
||||
{ "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64,
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
|
||||
{ "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) },
|
||||
{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
|
||||
{ "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16,
|
||||
SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
|
||||
.fixups = &atmel_global_protection_fixups },
|
||||
{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32,
|
||||
SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
|
||||
.fixups = &atmel_global_protection_fixups },
|
||||
{ "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64,
|
||||
SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
|
||||
.fixups = &atmel_global_protection_fixups },
|
||||
|
||||
{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
|
||||
};
|
||||
|
||||
static void atmel_default_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->flags |= SNOR_F_HAS_LOCK;
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups atmel_fixups = {
|
||||
.default_init = atmel_default_init,
|
||||
};
|
||||
|
||||
const struct spi_nor_manufacturer spi_nor_atmel = {
|
||||
.name = "atmel",
|
||||
.parts = atmel_parts,
|
||||
.nparts = ARRAY_SIZE(atmel_parts),
|
||||
.fixups = &atmel_fixups,
|
||||
};
|
||||
|
|
|
@ -320,7 +320,7 @@ static const struct spi_nor_controller_ops hisi_controller_ops = {
|
|||
.write = hisi_spi_nor_write,
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* Get spi flash device information and register it as a mtd device.
|
||||
*/
|
||||
static int hisi_spi_nor_register(struct device_node *np,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,6 +26,9 @@ enum spi_nor_option_flags {
|
|||
SNOR_F_HAS_SR_TB_BIT6 = BIT(11),
|
||||
SNOR_F_HAS_4BIT_BP = BIT(12),
|
||||
SNOR_F_HAS_SR_BP3_BIT6 = BIT(13),
|
||||
SNOR_F_IO_MODE_EN_VOLATILE = BIT(14),
|
||||
SNOR_F_SOFT_RESET = BIT(15),
|
||||
SNOR_F_SWP_IS_VOLATILE = BIT(16),
|
||||
};
|
||||
|
||||
struct spi_nor_read_command {
|
||||
|
@ -62,6 +65,7 @@ enum spi_nor_read_command_index {
|
|||
SNOR_CMD_READ_1_8_8,
|
||||
SNOR_CMD_READ_8_8_8,
|
||||
SNOR_CMD_READ_1_8_8_DTR,
|
||||
SNOR_CMD_READ_8_8_8_DTR,
|
||||
|
||||
SNOR_CMD_READ_MAX
|
||||
};
|
||||
|
@ -78,6 +82,7 @@ enum spi_nor_pp_command_index {
|
|||
SNOR_CMD_PP_1_1_8,
|
||||
SNOR_CMD_PP_1_8_8,
|
||||
SNOR_CMD_PP_8_8_8,
|
||||
SNOR_CMD_PP_8_8_8_DTR,
|
||||
|
||||
SNOR_CMD_PP_MAX
|
||||
};
|
||||
|
@ -189,7 +194,12 @@ struct spi_nor_locking_ops {
|
|||
* Serial Flash Discoverable Parameters (SFDP) tables.
|
||||
*
|
||||
* @size: the flash memory density in bytes.
|
||||
* @writesize Minimal writable flash unit size. Defaults to 1. Set to
|
||||
* ECC unit size for ECC-ed flashes.
|
||||
* @page_size: the page size of the SPI NOR flash memory.
|
||||
* @rdsr_dummy: dummy cycles needed for Read Status Register command.
|
||||
* @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register
|
||||
* command.
|
||||
* @hwcaps: describes the read and page program hardware
|
||||
* capabilities.
|
||||
* @reads: read capabilities ordered by priority: the higher index
|
||||
|
@ -198,6 +208,7 @@ struct spi_nor_locking_ops {
|
|||
* higher index in the array, the higher priority.
|
||||
* @erase_map: the erase map parsed from the SFDP Sector Map Parameter
|
||||
* Table.
|
||||
* @octal_dtr_enable: enables SPI NOR octal DTR mode.
|
||||
* @quad_enable: enables SPI NOR quad mode.
|
||||
* @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode.
|
||||
* @convert_addr: converts an absolute address into something the flash
|
||||
|
@ -211,7 +222,10 @@ struct spi_nor_locking_ops {
|
|||
*/
|
||||
struct spi_nor_flash_parameter {
|
||||
u64 size;
|
||||
u32 writesize;
|
||||
u32 page_size;
|
||||
u8 rdsr_dummy;
|
||||
u8 rdsr_addr_nbytes;
|
||||
|
||||
struct spi_nor_hwcaps hwcaps;
|
||||
struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
|
||||
|
@ -219,6 +233,7 @@ struct spi_nor_flash_parameter {
|
|||
|
||||
struct spi_nor_erase_map erase_map;
|
||||
|
||||
int (*octal_dtr_enable)(struct spi_nor *nor, bool enable);
|
||||
int (*quad_enable)(struct spi_nor *nor);
|
||||
int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable);
|
||||
u32 (*convert_addr)(struct spi_nor *nor, u32 addr);
|
||||
|
@ -311,6 +326,18 @@ struct flash_info {
|
|||
* BP3 is bit 6 of status register.
|
||||
* Must be used with SPI_NOR_4BIT_BP.
|
||||
*/
|
||||
#define SPI_NOR_OCTAL_DTR_READ BIT(19) /* Flash supports octal DTR Read. */
|
||||
#define SPI_NOR_OCTAL_DTR_PP BIT(20) /* Flash supports Octal DTR Page Program */
|
||||
#define SPI_NOR_IO_MODE_EN_VOLATILE BIT(21) /*
|
||||
* Flash enables the best
|
||||
* available I/O mode via a
|
||||
* volatile bit.
|
||||
*/
|
||||
#define SPI_NOR_SWP_IS_VOLATILE BIT(22) /*
|
||||
* Flash has volatile software write
|
||||
* protection bits. Usually these will
|
||||
* power-up in a write-protected state.
|
||||
*/
|
||||
|
||||
/* Part specific fixup hooks. */
|
||||
const struct spi_nor_fixups *fixups;
|
||||
|
@ -399,6 +426,9 @@ extern const struct spi_nor_manufacturer spi_nor_winbond;
|
|||
extern const struct spi_nor_manufacturer spi_nor_xilinx;
|
||||
extern const struct spi_nor_manufacturer spi_nor_xmc;
|
||||
|
||||
void spi_nor_spimem_setup_op(const struct spi_nor *nor,
|
||||
struct spi_mem_op *op,
|
||||
const enum spi_nor_protocol proto);
|
||||
int spi_nor_write_enable(struct spi_nor *nor);
|
||||
int spi_nor_write_disable(struct spi_nor *nor);
|
||||
int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable);
|
||||
|
@ -409,6 +439,9 @@ void spi_nor_unlock_and_unprep(struct spi_nor *nor);
|
|||
int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor);
|
||||
int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor);
|
||||
int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor);
|
||||
int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
|
||||
int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len);
|
||||
int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1);
|
||||
|
||||
int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr);
|
||||
ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
|
||||
|
@ -418,6 +451,11 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
|
|||
|
||||
int spi_nor_hwcaps_read2cmd(u32 hwcaps);
|
||||
u8 spi_nor_convert_3to4_read(u8 opcode);
|
||||
void spi_nor_set_read_settings(struct spi_nor_read_command *read,
|
||||
u8 num_mode_clocks,
|
||||
u8 num_wait_states,
|
||||
u8 opcode,
|
||||
enum spi_nor_protocol proto);
|
||||
void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode,
|
||||
enum spi_nor_protocol proto);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
static const struct flash_info esmt_parts[] = {
|
||||
/* ESMT */
|
||||
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64,
|
||||
SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64,
|
||||
SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
{ "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128,
|
||||
|
|
|
@ -10,23 +10,16 @@
|
|||
|
||||
static const struct flash_info intel_parts[] = {
|
||||
/* Intel/Numonyx -- xxxs33b */
|
||||
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
|
||||
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
|
||||
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
|
||||
};
|
||||
|
||||
static void intel_default_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->flags |= SNOR_F_HAS_LOCK;
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups intel_fixups = {
|
||||
.default_init = intel_default_init,
|
||||
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32,
|
||||
SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64,
|
||||
SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128,
|
||||
SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
};
|
||||
|
||||
const struct spi_nor_manufacturer spi_nor_intel = {
|
||||
.name = "intel",
|
||||
.parts = intel_parts,
|
||||
.nparts = ARRAY_SIZE(intel_parts),
|
||||
.fixups = &intel_fixups,
|
||||
};
|
||||
|
|
|
@ -8,10 +8,123 @@
|
|||
|
||||
#include "core.h"
|
||||
|
||||
#define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */
|
||||
#define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile register */
|
||||
#define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile register */
|
||||
#define SPINOR_REG_MT_CFR0V 0x00 /* For setting octal DTR mode */
|
||||
#define SPINOR_REG_MT_CFR1V 0x01 /* For setting dummy cycles */
|
||||
#define SPINOR_MT_OCT_DTR 0xe7 /* Enable Octal DTR. */
|
||||
#define SPINOR_MT_EXSPI 0xff /* Enable Extended SPI (default) */
|
||||
|
||||
static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor, bool enable)
|
||||
{
|
||||
struct spi_mem_op op;
|
||||
u8 *buf = nor->bouncebuf;
|
||||
int ret;
|
||||
|
||||
if (enable) {
|
||||
/* Use 20 dummy cycles for memory array reads. */
|
||||
ret = spi_nor_write_enable(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*buf = 20;
|
||||
op = (struct spi_mem_op)
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
|
||||
SPI_MEM_OP_ADDR(3, SPINOR_REG_MT_CFR1V, 1),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_OUT(1, buf, 1));
|
||||
|
||||
ret = spi_mem_exec_op(nor->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_nor_write_enable(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
*buf = SPINOR_MT_OCT_DTR;
|
||||
else
|
||||
*buf = SPINOR_MT_EXSPI;
|
||||
|
||||
op = (struct spi_mem_op)
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
|
||||
SPI_MEM_OP_ADDR(enable ? 3 : 4,
|
||||
SPINOR_REG_MT_CFR0V, 1),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_OUT(1, buf, 1));
|
||||
|
||||
if (!enable)
|
||||
spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
|
||||
|
||||
ret = spi_mem_exec_op(nor->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read flash ID to make sure the switch was successful. */
|
||||
op = (struct spi_mem_op)
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
|
||||
SPI_MEM_OP_NO_ADDR,
|
||||
SPI_MEM_OP_DUMMY(enable ? 8 : 0, 1),
|
||||
SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2),
|
||||
buf, 1));
|
||||
|
||||
if (enable)
|
||||
spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
|
||||
|
||||
ret = spi_mem_exec_op(nor->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (memcmp(buf, nor->info->id, nor->info->id_len))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mt35xu512aba_default_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->params->octal_dtr_enable = spi_nor_micron_octal_dtr_enable;
|
||||
}
|
||||
|
||||
static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
|
||||
{
|
||||
/* Set the Fast Read settings. */
|
||||
nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
|
||||
spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR],
|
||||
0, 20, SPINOR_OP_MT_DTR_RD,
|
||||
SNOR_PROTO_8_8_8_DTR);
|
||||
|
||||
nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
|
||||
nor->params->rdsr_dummy = 8;
|
||||
nor->params->rdsr_addr_nbytes = 0;
|
||||
|
||||
/*
|
||||
* The BFPT quad enable field is set to a reserved value so the quad
|
||||
* enable function is ignored by spi_nor_parse_bfpt(). Make sure we
|
||||
* disable it.
|
||||
*/
|
||||
nor->params->quad_enable = NULL;
|
||||
}
|
||||
|
||||
static struct spi_nor_fixups mt35xu512aba_fixups = {
|
||||
.default_init = mt35xu512aba_default_init,
|
||||
.post_sfdp = mt35xu512aba_post_sfdp_fixup,
|
||||
};
|
||||
|
||||
static const struct flash_info micron_parts[] = {
|
||||
{ "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512,
|
||||
SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ |
|
||||
SPI_NOR_4B_OPCODES) },
|
||||
SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ |
|
||||
SPI_NOR_OCTAL_DTR_PP |
|
||||
SPI_NOR_IO_MODE_EN_VOLATILE)
|
||||
.fixups = &mt35xu512aba_fixups},
|
||||
{ "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048,
|
||||
SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ |
|
||||
SPI_NOR_4B_OPCODES) },
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (C) 2014, Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
|
@ -19,6 +20,11 @@
|
|||
#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */
|
||||
#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */
|
||||
#define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */
|
||||
#define SFDP_PROFILE1_ID 0xff05 /* xSPI Profile 1.0 table. */
|
||||
#define SFDP_SCCR_MAP_ID 0xff87 /*
|
||||
* Status, Control and Configuration
|
||||
* Register Map.
|
||||
*/
|
||||
|
||||
#define SFDP_SIGNATURE 0x50444653U
|
||||
|
||||
|
@ -59,7 +65,7 @@ struct sfdp_bfpt_read {
|
|||
|
||||
struct sfdp_bfpt_erase {
|
||||
/*
|
||||
* The half-word at offset <shift> in DWORD <dwoard> encodes the
|
||||
* The half-word at offset <shift> in DWORD <dword> encodes the
|
||||
* op code and erase sector size to be used by Sector Erase commands.
|
||||
*/
|
||||
u32 dword;
|
||||
|
@ -602,10 +608,32 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
|
|||
break;
|
||||
}
|
||||
|
||||
/* Soft Reset support. */
|
||||
if (bfpt.dwords[BFPT_DWORD(16)] & BFPT_DWORD16_SWRST_EN_RST)
|
||||
nor->flags |= SNOR_F_SOFT_RESET;
|
||||
|
||||
/* Stop here if not JESD216 rev C or later. */
|
||||
if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B)
|
||||
return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt,
|
||||
params);
|
||||
/* 8D-8D-8D command extension. */
|
||||
switch (bfpt.dwords[BFPT_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) {
|
||||
case BFPT_DWORD18_CMD_EXT_REP:
|
||||
nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
|
||||
break;
|
||||
|
||||
case BFPT_DWORD18_CMD_EXT_INV:
|
||||
nor->cmd_ext_type = SPI_NOR_EXT_INVERT;
|
||||
break;
|
||||
|
||||
case BFPT_DWORD18_CMD_EXT_RES:
|
||||
dev_dbg(nor->dev, "Reserved command extension used\n");
|
||||
break;
|
||||
|
||||
case BFPT_DWORD18_CMD_EXT_16B:
|
||||
dev_dbg(nor->dev, "16-bit opcodes not supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params);
|
||||
}
|
||||
|
@ -1047,9 +1075,16 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
|
|||
}
|
||||
|
||||
/* 4BAIT is the only SFDP table that indicates page program support. */
|
||||
if (pp_hwcaps & SNOR_HWCAPS_PP)
|
||||
if (pp_hwcaps & SNOR_HWCAPS_PP) {
|
||||
spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP],
|
||||
SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1);
|
||||
/*
|
||||
* Since xSPI Page Program opcode is backward compatible with
|
||||
* Legacy SPI, use Legacy SPI opcode there as well.
|
||||
*/
|
||||
spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_8_8_8_DTR],
|
||||
SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR);
|
||||
}
|
||||
if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4)
|
||||
spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_1_4],
|
||||
SPINOR_OP_PP_1_1_4_4B,
|
||||
|
@ -1083,6 +1118,131 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define PROFILE1_DWORD1_RDSR_ADDR_BYTES BIT(29)
|
||||
#define PROFILE1_DWORD1_RDSR_DUMMY BIT(28)
|
||||
#define PROFILE1_DWORD1_RD_FAST_CMD GENMASK(15, 8)
|
||||
#define PROFILE1_DWORD4_DUMMY_200MHZ GENMASK(11, 7)
|
||||
#define PROFILE1_DWORD5_DUMMY_166MHZ GENMASK(31, 27)
|
||||
#define PROFILE1_DWORD5_DUMMY_133MHZ GENMASK(21, 17)
|
||||
#define PROFILE1_DWORD5_DUMMY_100MHZ GENMASK(11, 7)
|
||||
|
||||
/**
|
||||
* spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @profile1_header: pointer to the 'struct sfdp_parameter_header' describing
|
||||
* the Profile 1.0 Table length and version.
|
||||
* @params: pointer to the 'struct spi_nor_flash_parameter' to be.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
static int spi_nor_parse_profile1(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *profile1_header,
|
||||
struct spi_nor_flash_parameter *params)
|
||||
{
|
||||
u32 *dwords, addr;
|
||||
size_t len;
|
||||
int ret;
|
||||
u8 dummy, opcode;
|
||||
|
||||
len = profile1_header->length * sizeof(*dwords);
|
||||
dwords = kmalloc(len, GFP_KERNEL);
|
||||
if (!dwords)
|
||||
return -ENOMEM;
|
||||
|
||||
addr = SFDP_PARAM_HEADER_PTP(profile1_header);
|
||||
ret = spi_nor_read_sfdp(nor, addr, len, dwords);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
le32_to_cpu_array(dwords, profile1_header->length);
|
||||
|
||||
/* Get 8D-8D-8D fast read opcode and dummy cycles. */
|
||||
opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, dwords[0]);
|
||||
|
||||
/* Set the Read Status Register dummy cycles and dummy address bytes. */
|
||||
if (dwords[0] & PROFILE1_DWORD1_RDSR_DUMMY)
|
||||
params->rdsr_dummy = 8;
|
||||
else
|
||||
params->rdsr_dummy = 4;
|
||||
|
||||
if (dwords[0] & PROFILE1_DWORD1_RDSR_ADDR_BYTES)
|
||||
params->rdsr_addr_nbytes = 4;
|
||||
else
|
||||
params->rdsr_addr_nbytes = 0;
|
||||
|
||||
/*
|
||||
* We don't know what speed the controller is running at. Find the
|
||||
* dummy cycles for the fastest frequency the flash can run at to be
|
||||
* sure we are never short of dummy cycles. A value of 0 means the
|
||||
* frequency is not supported.
|
||||
*
|
||||
* Default to PROFILE1_DUMMY_DEFAULT if we don't find anything, and let
|
||||
* flashes set the correct value if needed in their fixup hooks.
|
||||
*/
|
||||
dummy = FIELD_GET(PROFILE1_DWORD4_DUMMY_200MHZ, dwords[3]);
|
||||
if (!dummy)
|
||||
dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_166MHZ, dwords[4]);
|
||||
if (!dummy)
|
||||
dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_133MHZ, dwords[4]);
|
||||
if (!dummy)
|
||||
dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_100MHZ, dwords[4]);
|
||||
if (!dummy)
|
||||
dev_dbg(nor->dev,
|
||||
"Can't find dummy cycles from Profile 1.0 table\n");
|
||||
|
||||
/* Round up to an even value to avoid tripping controllers up. */
|
||||
dummy = round_up(dummy, 2);
|
||||
|
||||
/* Update the fast read settings. */
|
||||
spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR],
|
||||
0, dummy, opcode,
|
||||
SNOR_PROTO_8_8_8_DTR);
|
||||
|
||||
out:
|
||||
kfree(dwords);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE BIT(31)
|
||||
|
||||
/**
|
||||
* spi_nor_parse_sccr() - Parse the Status, Control and Configuration Register
|
||||
* Map.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @sccr_header: pointer to the 'struct sfdp_parameter_header' describing
|
||||
* the SCCR Map table length and version.
|
||||
* @params: pointer to the 'struct spi_nor_flash_parameter' to be.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
static int spi_nor_parse_sccr(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *sccr_header,
|
||||
struct spi_nor_flash_parameter *params)
|
||||
{
|
||||
u32 *dwords, addr;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
len = sccr_header->length * sizeof(*dwords);
|
||||
dwords = kmalloc(len, GFP_KERNEL);
|
||||
if (!dwords)
|
||||
return -ENOMEM;
|
||||
|
||||
addr = SFDP_PARAM_HEADER_PTP(sccr_header);
|
||||
ret = spi_nor_read_sfdp(nor, addr, len, dwords);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
le32_to_cpu_array(dwords, sccr_header->length);
|
||||
|
||||
if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, dwords[22]))
|
||||
nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
|
||||
|
||||
out:
|
||||
kfree(dwords);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
|
@ -1184,6 +1344,14 @@ int spi_nor_parse_sfdp(struct spi_nor *nor,
|
|||
err = spi_nor_parse_4bait(nor, param_header, params);
|
||||
break;
|
||||
|
||||
case SFDP_PROFILE1_ID:
|
||||
err = spi_nor_parse_profile1(nor, param_header, params);
|
||||
break;
|
||||
|
||||
case SFDP_SCCR_MAP_ID:
|
||||
err = spi_nor_parse_sccr(nor, param_header, params);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -90,6 +90,14 @@ struct sfdp_bfpt {
|
|||
#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20)
|
||||
#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */
|
||||
|
||||
#define BFPT_DWORD16_SWRST_EN_RST BIT(12)
|
||||
|
||||
#define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29)
|
||||
#define BFPT_DWORD18_CMD_EXT_REP (0x0UL << 29) /* Repeat */
|
||||
#define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */
|
||||
#define BFPT_DWORD18_CMD_EXT_RES (0x2UL << 29) /* Reserved */
|
||||
#define BFPT_DWORD18_CMD_EXT_16B (0x3UL << 29) /* 16-bit opcode */
|
||||
|
||||
struct sfdp_parameter_header {
|
||||
u8 id_lsb;
|
||||
u8 minor;
|
||||
|
|
|
@ -8,6 +8,173 @@
|
|||
|
||||
#include "core.h"
|
||||
|
||||
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
|
||||
#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
|
||||
#define SPINOR_REG_CYPRESS_CFR2V 0x00800003
|
||||
#define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb
|
||||
#define SPINOR_REG_CYPRESS_CFR3V 0x00800004
|
||||
#define SPINOR_REG_CYPRESS_CFR3V_PGSZ BIT(4) /* Page size. */
|
||||
#define SPINOR_REG_CYPRESS_CFR5V 0x00800006
|
||||
#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN 0x3
|
||||
#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS 0
|
||||
#define SPINOR_OP_CYPRESS_RD_FAST 0xee
|
||||
|
||||
/**
|
||||
* spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @enable: whether to enable or disable Octal DTR
|
||||
*
|
||||
* This also sets the memory access latency cycles to 24 to allow the flash to
|
||||
* run at up to 200MHz.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor, bool enable)
|
||||
{
|
||||
struct spi_mem_op op;
|
||||
u8 *buf = nor->bouncebuf;
|
||||
int ret;
|
||||
|
||||
if (enable) {
|
||||
/* Use 24 dummy cycles for memory array reads. */
|
||||
ret = spi_nor_write_enable(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
|
||||
op = (struct spi_mem_op)
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
|
||||
SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR2V,
|
||||
1),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_OUT(1, buf, 1));
|
||||
|
||||
ret = spi_mem_exec_op(nor->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nor->read_dummy = 24;
|
||||
}
|
||||
|
||||
/* Set/unset the octal and DTR enable bits. */
|
||||
ret = spi_nor_write_enable(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
*buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
|
||||
else
|
||||
*buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS;
|
||||
|
||||
op = (struct spi_mem_op)
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
|
||||
SPI_MEM_OP_ADDR(enable ? 3 : 4,
|
||||
SPINOR_REG_CYPRESS_CFR5V,
|
||||
1),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_OUT(1, buf, 1));
|
||||
|
||||
if (!enable)
|
||||
spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
|
||||
|
||||
ret = spi_mem_exec_op(nor->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read flash ID to make sure the switch was successful. */
|
||||
op = (struct spi_mem_op)
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
|
||||
SPI_MEM_OP_ADDR(enable ? 4 : 0, 0, 1),
|
||||
SPI_MEM_OP_DUMMY(enable ? 3 : 0, 1),
|
||||
SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2),
|
||||
buf, 1));
|
||||
|
||||
if (enable)
|
||||
spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
|
||||
|
||||
ret = spi_mem_exec_op(nor->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (memcmp(buf, nor->info->id, nor->info->id_len))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s28hs512t_default_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->params->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable;
|
||||
nor->params->writesize = 16;
|
||||
}
|
||||
|
||||
static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor)
|
||||
{
|
||||
/*
|
||||
* On older versions of the flash the xSPI Profile 1.0 table has the
|
||||
* 8D-8D-8D Fast Read opcode as 0x00. But it actually should be 0xEE.
|
||||
*/
|
||||
if (nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0)
|
||||
nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode =
|
||||
SPINOR_OP_CYPRESS_RD_FAST;
|
||||
|
||||
/* This flash is also missing the 4-byte Page Program opcode bit. */
|
||||
spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP],
|
||||
SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1);
|
||||
/*
|
||||
* Since xSPI Page Program opcode is backward compatible with
|
||||
* Legacy SPI, use Legacy SPI opcode there as well.
|
||||
*/
|
||||
spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
|
||||
SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR);
|
||||
|
||||
/*
|
||||
* The xSPI Profile 1.0 table advertises the number of additional
|
||||
* address bytes needed for Read Status Register command as 0 but the
|
||||
* actual value for that is 4.
|
||||
*/
|
||||
nor->params->rdsr_addr_nbytes = 4;
|
||||
}
|
||||
|
||||
static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
const struct sfdp_bfpt *bfpt,
|
||||
struct spi_nor_flash_parameter *params)
|
||||
{
|
||||
/*
|
||||
* The BFPT table advertises a 512B page size but the page size is
|
||||
* actually configurable (with the default being 256B). Read from
|
||||
* CFR3V[4] and set the correct size.
|
||||
*/
|
||||
struct spi_mem_op op =
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
|
||||
SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR3V, 1),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
|
||||
int ret;
|
||||
|
||||
ret = spi_mem_exec_op(nor->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ)
|
||||
params->page_size = 512;
|
||||
else
|
||||
params->page_size = 256;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_nor_fixups s28hs512t_fixups = {
|
||||
.default_init = s28hs512t_default_init,
|
||||
.post_sfdp = s28hs512t_post_sfdp_fixup,
|
||||
.post_bfpt = s28hs512t_post_bfpt_fixup,
|
||||
};
|
||||
|
||||
static int
|
||||
s25fs_s_post_bfpt_fixups(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
|
@ -104,6 +271,11 @@ static const struct flash_info spansion_parts[] = {
|
|||
SPI_NOR_4B_OPCODES) },
|
||||
{ "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1,
|
||||
SPI_NOR_NO_ERASE) },
|
||||
{ "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256,
|
||||
SECT_4K | SPI_NOR_OCTAL_DTR_READ |
|
||||
SPI_NOR_OCTAL_DTR_PP)
|
||||
.fixups = &s28hs512t_fixups,
|
||||
},
|
||||
};
|
||||
|
||||
static void spansion_post_sfdp_fixups(struct spi_nor *nor)
|
||||
|
|
|
@ -11,26 +11,28 @@
|
|||
static const struct flash_info sst_parts[] = {
|
||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8,
|
||||
SECT_4K | SST_WRITE) },
|
||||
SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16,
|
||||
SECT_4K | SST_WRITE) },
|
||||
SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32,
|
||||
SECT_4K | SST_WRITE) },
|
||||
SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64,
|
||||
SECT_4K | SST_WRITE) },
|
||||
{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },
|
||||
SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128,
|
||||
SECT_4K | SPI_NOR_4BIT_BP | SPI_NOR_HAS_LOCK |
|
||||
SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1,
|
||||
SECT_4K | SST_WRITE) },
|
||||
SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2,
|
||||
SECT_4K | SST_WRITE) },
|
||||
SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4,
|
||||
SECT_4K | SST_WRITE) },
|
||||
{ "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) },
|
||||
SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8,
|
||||
SECT_4K | SST_WRITE) },
|
||||
SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16,
|
||||
SECT_4K | SST_WRITE) },
|
||||
SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) },
|
||||
{ "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32,
|
||||
SECT_4K | SPI_NOR_DUAL_READ |
|
||||
SPI_NOR_QUAD_READ) },
|
||||
|
@ -127,11 +129,6 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void sst_default_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->flags |= SNOR_F_HAS_LOCK;
|
||||
}
|
||||
|
||||
static void sst_post_sfdp_fixups(struct spi_nor *nor)
|
||||
{
|
||||
if (nor->info->flags & SST_WRITE)
|
||||
|
@ -139,7 +136,6 @@ static void sst_post_sfdp_fixups(struct spi_nor *nor)
|
|||
}
|
||||
|
||||
static const struct spi_nor_fixups sst_fixups = {
|
||||
.default_init = sst_default_init,
|
||||
.post_sfdp = sst_post_sfdp_fixups,
|
||||
};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand-ecc-sw-hamming.h>
|
||||
|
||||
#include "mtd_test.h"
|
||||
|
||||
|
@ -119,13 +119,13 @@ static void no_bit_error(void *error_data, void *error_ecc,
|
|||
static int no_bit_error_verify(void *error_data, void *error_ecc,
|
||||
void *correct_data, const size_t size)
|
||||
{
|
||||
bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
|
||||
unsigned char calc_ecc[3];
|
||||
int ret;
|
||||
|
||||
__nand_calculate_ecc(error_data, size, calc_ecc,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
ecc_sw_hamming_calculate(error_data, size, calc_ecc, sm_order);
|
||||
ret = ecc_sw_hamming_correct(error_data, error_ecc, calc_ecc, size,
|
||||
sm_order);
|
||||
if (ret == 0 && !memcmp(correct_data, error_data, size))
|
||||
return 0;
|
||||
|
||||
|
@ -149,13 +149,13 @@ static void single_bit_error_in_ecc(void *error_data, void *error_ecc,
|
|||
static int single_bit_error_correct(void *error_data, void *error_ecc,
|
||||
void *correct_data, const size_t size)
|
||||
{
|
||||
bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
|
||||
unsigned char calc_ecc[3];
|
||||
int ret;
|
||||
|
||||
__nand_calculate_ecc(error_data, size, calc_ecc,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
ecc_sw_hamming_calculate(error_data, size, calc_ecc, sm_order);
|
||||
ret = ecc_sw_hamming_correct(error_data, error_ecc, calc_ecc, size,
|
||||
sm_order);
|
||||
if (ret == 1 && !memcmp(correct_data, error_data, size))
|
||||
return 0;
|
||||
|
||||
|
@ -186,13 +186,13 @@ static void double_bit_error_in_ecc(void *error_data, void *error_ecc,
|
|||
static int double_bit_error_detect(void *error_data, void *error_ecc,
|
||||
void *correct_data, const size_t size)
|
||||
{
|
||||
bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
|
||||
unsigned char calc_ecc[3];
|
||||
int ret;
|
||||
|
||||
__nand_calculate_ecc(error_data, size, calc_ecc,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
ecc_sw_hamming_calculate(error_data, size, calc_ecc, sm_order);
|
||||
ret = ecc_sw_hamming_correct(error_data, error_ecc, calc_ecc, size,
|
||||
sm_order);
|
||||
|
||||
return (ret == -EBADMSG) ? 0 : -EINVAL;
|
||||
}
|
||||
|
@ -248,6 +248,7 @@ static void dump_data_ecc(void *error_data, void *error_ecc, void *correct_data,
|
|||
|
||||
static int nand_ecc_test_run(const size_t size)
|
||||
{
|
||||
bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
|
||||
int i;
|
||||
int err = 0;
|
||||
void *error_data;
|
||||
|
@ -266,9 +267,7 @@ static int nand_ecc_test_run(const size_t size)
|
|||
}
|
||||
|
||||
prandom_bytes(correct_data, size);
|
||||
__nand_calculate_ecc(correct_data, size, correct_ecc,
|
||||
IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
|
||||
|
||||
ecc_sw_hamming_calculate(correct_data, size, correct_ecc, sm_order);
|
||||
for (i = 0; i < ARRAY_SIZE(nand_ecc_test); i++) {
|
||||
nand_ecc_test[i].prepare(error_data, error_ecc,
|
||||
correct_data, correct_ecc, size);
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
* struct mtd_dev_param - MTD device parameter description data structure.
|
||||
* @name: MTD character device node path, MTD device name, or MTD device number
|
||||
* string
|
||||
* @ubi_num: UBI number
|
||||
* @vid_hdr_offs: VID header offset
|
||||
* @max_beb_per1024: maximum expected number of bad PEBs per 1024 PEBs
|
||||
*/
|
||||
|
|
|
@ -1290,7 +1290,7 @@ static int is_error_sane(int err)
|
|||
* @ubi: UBI device description object
|
||||
* @from: physical eraseblock number from where to copy
|
||||
* @to: physical eraseblock number where to copy
|
||||
* @vid_hdr: VID header of the @from physical eraseblock
|
||||
* @vidb: data structure from where the VID header is derived
|
||||
*
|
||||
* This function copies logical eraseblock from physical eraseblock @from to
|
||||
* physical eraseblock @to. The @vid_hdr buffer may be changed by this
|
||||
|
@ -1463,6 +1463,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
|
|||
/**
|
||||
* print_rsvd_warning - warn about not having enough reserved PEBs.
|
||||
* @ubi: UBI device description object
|
||||
* @ai: UBI attach info object
|
||||
*
|
||||
* This is a helper function for 'ubi_eba_init()' which is called when UBI
|
||||
* cannot reserve enough PEBs for bad block handling. This function makes a
|
||||
|
|
|
@ -439,7 +439,7 @@ static int gluebi_resized(struct ubi_volume_info *vi)
|
|||
* gluebi_notify - UBI notification handler.
|
||||
* @nb: registered notifier block
|
||||
* @l: notification type
|
||||
* @ptr: pointer to the &struct ubi_notification object
|
||||
* @ns_ptr: pointer to the &struct ubi_notification object
|
||||
*/
|
||||
static int gluebi_notify(struct notifier_block *nb, unsigned long l,
|
||||
void *ns_ptr)
|
||||
|
|
|
@ -450,7 +450,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_read);
|
|||
* ubi_leb_read_sg - read data into a scatter gather list.
|
||||
* @desc: volume descriptor
|
||||
* @lnum: logical eraseblock number to read from
|
||||
* @buf: buffer where to store the read data
|
||||
* @sgl: UBI scatter gather list to store the read data
|
||||
* @offset: offset within the logical eraseblock to read from
|
||||
* @len: how many bytes to read
|
||||
* @check: whether UBI has to check the read data's CRC or not.
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue