Raw NAND core:
* Allow SDR timings to be nacked * Bring support for NV-DDR timings which involved a number of small preparation changes to bring new helpers, properly introduce NV-DDR structures, fill them, differenciate them and pick the best timing set. * Add the necessary infrastructure to parse the new gpio-cs property which aims at enlarging the number of available CS when a hardware controller is too constrained. * Update dead URL * Silence static checker warning in nand_setup_interface() * BBT: - Fix corner case in bad block table handling * onfi: - Use more recent ONFI specification wording - Use the BIT() macro when possible Raw NAND controller drivers: * Atmel: - Ensure the data interface is supported. * Arasan: - Finer grain NV-DDR configuration - Rename the data interface register - Use the right DMA mask - Leverage additional GPIO CS - Ensure proper configuration for the asserted target - Add support for the NV-DDR interface - Fix a macro parameter * brcmnand: - Convert bindings to json-schema * OMAP: - Various fixes and style improvements - Add larger page NAND chips support * PL35X: - New driver * QCOM: - Avoid writing to obsolete register - Delete an unneeded bool conversion - Allow override of partition parser * Marvell: - Minor documentation correction - Add missing clk_disable_unprepare() on error in marvell_nfc_resume() * R852: - Use DEVICE_ATTR_RO() helper macro * MTK: - Remove redundant dev_err call in mtk_ecc_probe() * HISI504: - Remove redundant dev_err call in probe SPI-NAND core: * Light reorganisation for the introduction of a core resume handler * Fix double counting of ECC stats SPI-NAND manufacturer drivers: * Macronix: - Add support for serial NAND flash -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmDS290ACgkQJWrqGEe9 VoS77wgAgKnqfFwbqzdSecVmXai9roSNHi/aMpVJvvLHSTCV35iCcNNtrz0Jno56 t/8twVPPZUI6f3vPXk5+NjKRCb8Oif3oU+tNxqG6mwzrBRf5R80pYCMm+HGPWZaK +MolqGAbgFO088DTK5fGNtmbnb5fPT174RKRBGEH5NgK5M1q95uF8fIjBXDcjVe2 hvAyoqqC7i/bp40sxRtglgBpYYz+KUjCX11JvJPa5IuVU+8ezZSKJLSo5/CtgIKJ mRQ8p8myiUjZngcnebjTrAA5AwQ13ai9ofKG0pHkdxwOq4lSRNgKou0Hk5+oq+s0 BFEuXnUrdnJNU7+XV+kAqBoT6WOFzg== =I/9X -----END PGP SIGNATURE----- Merge tag 'nand/for-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux into mtd/next Raw NAND core: * Allow SDR timings to be nacked * Bring support for NV-DDR timings which involved a number of small preparation changes to bring new helpers, properly introduce NV-DDR structures, fill them, differenciate them and pick the best timing set. * Add the necessary infrastructure to parse the new gpio-cs property which aims at enlarging the number of available CS when a hardware controller is too constrained. * Update dead URL * Silence static checker warning in nand_setup_interface() * BBT: - Fix corner case in bad block table handling * onfi: - Use more recent ONFI specification wording - Use the BIT() macro when possible Raw NAND controller drivers: * Atmel: - Ensure the data interface is supported. * Arasan: - Finer grain NV-DDR configuration - Rename the data interface register - Use the right DMA mask - Leverage additional GPIO CS - Ensure proper configuration for the asserted target - Add support for the NV-DDR interface - Fix a macro parameter * brcmnand: - Convert bindings to json-schema * OMAP: - Various fixes and style improvements - Add larger page NAND chips support * PL35X: - New driver * QCOM: - Avoid writing to obsolete register - Delete an unneeded bool conversion - Allow override of partition parser * Marvell: - Minor documentation correction - Add missing clk_disable_unprepare() on error in marvell_nfc_resume() * R852: - Use DEVICE_ATTR_RO() helper macro * MTK: - Remove redundant dev_err call in mtk_ecc_probe() * HISI504: - Remove redundant dev_err call in probe SPI-NAND core: * Light reorganisation for the introduction of a core resume handler * Fix double counting of ECC stats SPI-NAND manufacturer drivers: * Macronix: - Add support for serial NAND flash
This commit is contained in:
commit
600d050944
@ -0,0 +1,131 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/arm,pl353-smc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ARM PL353 Static Memory Controller (SMC) device-tree bindings
|
||||
|
||||
maintainers:
|
||||
- Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
- Naga Sureshkumar Relli <naga.sureshkumar.relli@xilinx.com>
|
||||
|
||||
description:
|
||||
The PL353 Static Memory Controller is a bus where you can connect two kinds
|
||||
of memory interfaces, which are NAND and memory mapped interfaces (such as
|
||||
SRAM or NOR).
|
||||
|
||||
# We need a select here so we don't match all nodes with 'arm,primecell'
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: arm,pl353-smc-r2p1
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^memory-controller@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
items:
|
||||
- const: arm,pl353-smc-r2p1
|
||||
- const: arm,primecell
|
||||
|
||||
"#address-cells":
|
||||
const: 2
|
||||
|
||||
"#size-cells":
|
||||
const: 1
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description:
|
||||
Configuration registers for the host and sub-controllers.
|
||||
The three chip select regions are defined in 'ranges'.
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: clock for the memory device bus
|
||||
- description: main clock of the SMC
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: memclk
|
||||
- const: apb_pclk
|
||||
|
||||
ranges:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
description: |
|
||||
Memory bus areas for interacting with the devices. Reflects
|
||||
the memory layout with four integer values following:
|
||||
<cs-number> 0 <offset> <size>
|
||||
items:
|
||||
- description: NAND bank 0
|
||||
- description: NOR/SRAM bank 0
|
||||
- description: NOR/SRAM bank 1
|
||||
|
||||
interrupts: true
|
||||
|
||||
patternProperties:
|
||||
"@[0-3],[a-f0-9]+$":
|
||||
type: object
|
||||
description: |
|
||||
The child device node represents the controller connected to the SMC
|
||||
bus. The controller can be a NAND controller or a pair of any memory
|
||||
mapped controllers such as NOR and SRAM controllers.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
description:
|
||||
Compatible of memory controller.
|
||||
|
||||
reg:
|
||||
items:
|
||||
- items:
|
||||
- description: |
|
||||
Chip-select ID, as in the parent range property.
|
||||
minimum: 0
|
||||
maximum: 2
|
||||
- description: |
|
||||
Offset of the memory region requested by the device.
|
||||
- description: |
|
||||
Length of the memory region requested by the device.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clock-names
|
||||
- clocks
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- ranges
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
smcc: memory-controller@e000e000 {
|
||||
compatible = "arm,pl353-smc-r2p1", "arm,primecell";
|
||||
reg = <0xe000e000 0x0001000>;
|
||||
clock-names = "memclk", "apb_pclk";
|
||||
clocks = <&clkc 11>, <&clkc 44>;
|
||||
ranges = <0x0 0x0 0xe1000000 0x1000000 /* Nand CS region */
|
||||
0x1 0x0 0xe2000000 0x2000000 /* SRAM/NOR CS0 region */
|
||||
0x2 0x0 0xe4000000 0x2000000>; /* SRAM/NOR CS1 region */
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
|
||||
nfc0: nand-controller@0,0 {
|
||||
compatible = "arm,pl353-nand-r2p1";
|
||||
reg = <0 0 0x1000000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
@ -1,47 +0,0 @@
|
||||
Device tree bindings for ARM PL353 static memory controller
|
||||
|
||||
PL353 static memory controller supports two kinds of memory
|
||||
interfaces.i.e NAND and SRAM/NOR interfaces.
|
||||
The actual devices are instantiated from the child nodes of pl353 smc node.
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "arm,pl353-smc-r2p1", "arm,primecell".
|
||||
- reg : Controller registers map and length.
|
||||
- clock-names : List of input clock names - "memclk", "apb_pclk"
|
||||
(See clock bindings for details).
|
||||
- clocks : Clock phandles (see clock bindings for details).
|
||||
- address-cells : Must be 2.
|
||||
- size-cells : Must be 1.
|
||||
|
||||
Child nodes:
|
||||
For NAND the "arm,pl353-nand-r2p1" and for NOR the "cfi-flash" drivers are
|
||||
supported as child nodes.
|
||||
|
||||
for NAND partition information please refer the below file
|
||||
Documentation/devicetree/bindings/mtd/partition.txt
|
||||
|
||||
Example:
|
||||
smcc: memory-controller@e000e000
|
||||
compatible = "arm,pl353-smc-r2p1", "arm,primecell";
|
||||
clock-names = "memclk", "apb_pclk";
|
||||
clocks = <&clkc 11>, <&clkc 44>;
|
||||
reg = <0xe000e000 0x1000>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0x0 0x0 0xe1000000 0x1000000 //Nand CS Region
|
||||
0x1 0x0 0xe2000000 0x2000000 //SRAM/NOR CS Region
|
||||
0x2 0x0 0xe4000000 0x2000000>; //SRAM/NOR CS Region
|
||||
nand_0: flash@e1000000 {
|
||||
compatible = "arm,pl353-nand-r2p1"
|
||||
reg = <0 0 0x1000000>;
|
||||
(...)
|
||||
};
|
||||
nor0: flash@e2000000 {
|
||||
compatible = "cfi-flash";
|
||||
reg = <1 0 0x2000000>;
|
||||
};
|
||||
nor1: flash@e4000000 {
|
||||
compatible = "cfi-flash";
|
||||
reg = <2 0 0x2000000>;
|
||||
};
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/arm,pl353-nand-r2p1.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: PL353 NAND Controller device tree bindings
|
||||
|
||||
allOf:
|
||||
- $ref: "nand-controller.yaml"
|
||||
|
||||
maintainers:
|
||||
- Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
- Naga Sureshkumar Relli <naga.sureshkumar.relli@xilinx.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: arm,pl353-nand-r2p1
|
||||
|
||||
reg:
|
||||
items:
|
||||
- items:
|
||||
- description: CS with regard to the parent ranges property
|
||||
- description: Offset of the memory region requested by the device
|
||||
- description: Length of the memory region requested by the device
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
smcc: memory-controller@e000e000 {
|
||||
compatible = "arm,pl353-smc-r2p1", "arm,primecell";
|
||||
reg = <0xe000e000 0x0001000>;
|
||||
clock-names = "memclk", "apb_pclk";
|
||||
clocks = <&clkc 11>, <&clkc 44>;
|
||||
ranges = <0x0 0x0 0xe1000000 0x1000000 /* Nand CS region */
|
||||
0x1 0x0 0xe2000000 0x2000000 /* SRAM/NOR CS0 region */
|
||||
0x2 0x0 0xe4000000 0x2000000>; /* SRAM/NOR CS1 region */
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
|
||||
nfc0: nand-controller@0,0 {
|
||||
compatible = "arm,pl353-nand-r2p1";
|
||||
reg = <0 0 0x1000000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
@ -1,186 +0,0 @@
|
||||
* Broadcom STB NAND Controller
|
||||
|
||||
The Broadcom Set-Top Box NAND controller supports low-level access to raw NAND
|
||||
flash chips. It has a memory-mapped register interface for both control
|
||||
registers and for its data input/output buffer. On some SoCs, this controller is
|
||||
paired with a custom DMA engine (inventively named "Flash DMA") which supports
|
||||
basic PROGRAM and READ functions, among other features.
|
||||
|
||||
This controller was originally designed for STB SoCs (BCM7xxx) but is now
|
||||
available on a variety of Broadcom SoCs, including some BCM3xxx, BCM63xx, and
|
||||
iProc/Cygnus. Its history includes several similar (but not fully register
|
||||
compatible) versions.
|
||||
|
||||
Required properties:
|
||||
- compatible : May contain an SoC-specific compatibility string (see below)
|
||||
to account for any SoC-specific hardware bits that may be
|
||||
added on top of the base core controller.
|
||||
In addition, must contain compatibility information about
|
||||
the core NAND controller, of the following form:
|
||||
"brcm,brcmnand" and an appropriate version compatibility
|
||||
string, like "brcm,brcmnand-v7.0"
|
||||
Possible values:
|
||||
brcm,brcmnand-v2.1
|
||||
brcm,brcmnand-v2.2
|
||||
brcm,brcmnand-v4.0
|
||||
brcm,brcmnand-v5.0
|
||||
brcm,brcmnand-v6.0
|
||||
brcm,brcmnand-v6.1
|
||||
brcm,brcmnand-v6.2
|
||||
brcm,brcmnand-v7.0
|
||||
brcm,brcmnand-v7.1
|
||||
brcm,brcmnand-v7.2
|
||||
brcm,brcmnand-v7.3
|
||||
brcm,brcmnand
|
||||
- reg : the register start and length for NAND register region.
|
||||
(optional) Flash DMA register range (if present)
|
||||
(optional) NAND flash cache range (if at non-standard offset)
|
||||
- reg-names : a list of the names corresponding to the previous register
|
||||
ranges. Should contain "nand" and (optionally)
|
||||
"flash-dma" or "flash-edu" and/or "nand-cache".
|
||||
- interrupts : The NAND CTLRDY interrupt, (if Flash DMA is available)
|
||||
FLASH_DMA_DONE and if EDU is avaialble and used FLASH_EDU_DONE
|
||||
- interrupt-names : May be "nand_ctlrdy" or "flash_dma_done" or "flash_edu_done",
|
||||
if broken out as individual interrupts.
|
||||
May be "nand", if the SoC has the individual NAND
|
||||
interrupts multiplexed behind another custom piece of
|
||||
hardware
|
||||
- #address-cells : <1> - subnodes give the chip-select number
|
||||
- #size-cells : <0>
|
||||
|
||||
Optional properties:
|
||||
- clock : reference to the clock for the NAND controller
|
||||
- clock-names : "nand" (required for the above clock)
|
||||
- brcm,nand-has-wp : Some versions of this IP include a write-protect
|
||||
(WP) control bit. It is always available on >=
|
||||
v7.0. Use this property to describe the rare
|
||||
earlier versions of this core that include WP
|
||||
|
||||
-- Additional SoC-specific NAND controller properties --
|
||||
|
||||
The NAND controller is integrated differently on the variety of SoCs on which it
|
||||
is found. Part of this integration involves providing status and enable bits
|
||||
with which to control the 8 exposed NAND interrupts, as well as hardware for
|
||||
configuring the endianness of the data bus. On some SoCs, these features are
|
||||
handled via standard, modular components (e.g., their interrupts look like a
|
||||
normal IRQ chip), but on others, they are controlled in unique and interesting
|
||||
ways, sometimes with registers that lump multiple NAND-related functions
|
||||
together. The former case can be described simply by the standard interrupts
|
||||
properties in the main controller node. But for the latter exceptional cases,
|
||||
we define additional 'compatible' properties and associated register resources within the NAND controller node above.
|
||||
|
||||
- compatible: Can be one of several SoC-specific strings. Each SoC may have
|
||||
different requirements for its additional properties, as described below each
|
||||
bullet point below.
|
||||
|
||||
* "brcm,nand-bcm63138"
|
||||
- reg: (required) the 'NAND_INT_BASE' register range, with separate status
|
||||
and enable registers
|
||||
- reg-names: (required) "nand-int-base"
|
||||
|
||||
* "brcm,nand-bcm6368"
|
||||
- compatible: should contain "brcm,nand-bcm<soc>", "brcm,nand-bcm6368"
|
||||
- reg: (required) the 'NAND_INTR_BASE' register range, with combined status
|
||||
and enable registers, and boot address registers
|
||||
- reg-names: (required) "nand-int-base"
|
||||
|
||||
* "brcm,nand-iproc"
|
||||
- reg: (required) the "IDM" register range, for interrupt enable and APB
|
||||
bus access endianness configuration, and the "EXT" register range,
|
||||
for interrupt status/ack.
|
||||
- reg-names: (required) a list of the names corresponding to the previous
|
||||
register ranges. Should contain "iproc-idm" and "iproc-ext".
|
||||
|
||||
|
||||
* NAND chip-select
|
||||
|
||||
Each controller (compatible: "brcm,brcmnand") may contain one or more subnodes
|
||||
to represent enabled chip-selects which (may) contain NAND flash chips. Their
|
||||
properties are as follows.
|
||||
|
||||
Required properties:
|
||||
- compatible : should contain "brcm,nandcs"
|
||||
- reg : a single integer representing the chip-select
|
||||
number (e.g., 0, 1, 2, etc.)
|
||||
- #address-cells : see partition.txt
|
||||
- #size-cells : see partition.txt
|
||||
|
||||
Optional properties:
|
||||
- nand-ecc-strength : see nand-controller.yaml
|
||||
- nand-ecc-step-size : must be 512 or 1024. See nand-controller.yaml
|
||||
- nand-on-flash-bbt : boolean, to enable the on-flash BBT for this
|
||||
chip-select. See nand-controller.yaml
|
||||
- brcm,nand-oob-sector-size : integer, to denote the spare area sector size
|
||||
expected for the ECC layout in use. This size, in
|
||||
addition to the strength and step-size,
|
||||
determines how the hardware BCH engine will lay
|
||||
out the parity bytes it stores on the flash.
|
||||
This property can be automatically determined by
|
||||
the flash geometry (particularly the NAND page
|
||||
and OOB size) in many cases, but when booting
|
||||
from NAND, the boot controller has only a limited
|
||||
number of available options for its default ECC
|
||||
layout.
|
||||
|
||||
Each nandcs device node may optionally contain sub-nodes describing the flash
|
||||
partition mapping. See partition.txt for more detail.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
nand@f0442800 {
|
||||
compatible = "brcm,brcmnand-v7.0", "brcm,brcmnand";
|
||||
reg = <0xF0442800 0x600>,
|
||||
<0xF0443000 0x100>;
|
||||
reg-names = "nand", "flash-dma";
|
||||
interrupt-parent = <&hif_intr2_intc>;
|
||||
interrupts = <24>, <4>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nandcs@1 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <1>; // Chip select 1
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-strength = <12>;
|
||||
nand-ecc-step-size = <512>;
|
||||
|
||||
// Partitions
|
||||
#address-cells = <1>; // <2>, for 64-bit offset
|
||||
#size-cells = <1>; // <2>, for 64-bit length
|
||||
flash0.rootfs@0 {
|
||||
reg = <0 0x10000000>;
|
||||
};
|
||||
flash0@0 {
|
||||
reg = <0 0>; // MTDPART_SIZ_FULL
|
||||
};
|
||||
flash0.kernel@10000000 {
|
||||
reg = <0x10000000 0x400000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nand@10000200 {
|
||||
compatible = "brcm,nand-bcm63168", "brcm,nand-bcm6368",
|
||||
"brcm,brcmnand-v4.0", "brcm,brcmnand";
|
||||
reg = <0x10000200 0x180>,
|
||||
<0x10000600 0x200>,
|
||||
<0x100000b0 0x10>;
|
||||
reg-names = "nand", "nand-cache", "nand-int-base";
|
||||
interrupt-parent = <&periph_intc>;
|
||||
interrupts = <50>;
|
||||
clocks = <&periph_clk 20>;
|
||||
clock-names = "nand";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand0: nandcs@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-strength = <1>;
|
||||
nand-ecc-step-size = <512>;
|
||||
};
|
||||
};
|
242
Documentation/devicetree/bindings/mtd/brcm,brcmnand.yaml
Normal file
242
Documentation/devicetree/bindings/mtd/brcm,brcmnand.yaml
Normal file
@ -0,0 +1,242 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/brcm,brcmnand.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom STB NAND Controller
|
||||
|
||||
maintainers:
|
||||
- Brian Norris <computersforpeace@gmail.com>
|
||||
- Kamal Dasu <kdasu.kdev@gmail.com>
|
||||
|
||||
description: |
|
||||
The Broadcom Set-Top Box NAND controller supports low-level access to raw NAND
|
||||
flash chips. It has a memory-mapped register interface for both control
|
||||
registers and for its data input/output buffer. On some SoCs, this controller
|
||||
is paired with a custom DMA engine (inventively named "Flash DMA") which
|
||||
supports basic PROGRAM and READ functions, among other features.
|
||||
|
||||
This controller was originally designed for STB SoCs (BCM7xxx) but is now
|
||||
available on a variety of Broadcom SoCs, including some BCM3xxx, BCM63xx, and
|
||||
iProc/Cygnus. Its history includes several similar (but not fully register
|
||||
compatible) versions.
|
||||
|
||||
-- Additional SoC-specific NAND controller properties --
|
||||
|
||||
The NAND controller is integrated differently on the variety of SoCs on which
|
||||
it is found. Part of this integration involves providing status and enable
|
||||
bits with which to control the 8 exposed NAND interrupts, as well as hardware
|
||||
for configuring the endianness of the data bus. On some SoCs, these features
|
||||
are handled via standard, modular components (e.g., their interrupts look like
|
||||
a normal IRQ chip), but on others, they are controlled in unique and
|
||||
interesting ways, sometimes with registers that lump multiple NAND-related
|
||||
functions together. The former case can be described simply by the standard
|
||||
interrupts properties in the main controller node. But for the latter
|
||||
exceptional cases, we define additional 'compatible' properties and associated
|
||||
register resources within the NAND controller node above.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- brcm,brcmnand-v2.1
|
||||
- brcm,brcmnand-v2.2
|
||||
- brcm,brcmnand-v4.0
|
||||
- brcm,brcmnand-v5.0
|
||||
- brcm,brcmnand-v6.0
|
||||
- brcm,brcmnand-v6.1
|
||||
- brcm,brcmnand-v6.2
|
||||
- brcm,brcmnand-v7.0
|
||||
- brcm,brcmnand-v7.1
|
||||
- brcm,brcmnand-v7.2
|
||||
- brcm,brcmnand-v7.3
|
||||
- const: brcm,brcmnand
|
||||
- description: BCM63138 SoC-specific NAND controller
|
||||
items:
|
||||
- const: brcm,nand-bcm63138
|
||||
- enum:
|
||||
- brcm,brcmnand-v7.0
|
||||
- brcm,brcmnand-v7.1
|
||||
- const: brcm,brcmnand
|
||||
- description: iProc SoC-specific NAND controller
|
||||
items:
|
||||
- const: brcm,nand-iproc
|
||||
- const: brcm,brcmnand-v6.1
|
||||
- const: brcm,brcmnand
|
||||
- description: BCM63168 SoC-specific NAND controller
|
||||
items:
|
||||
- const: brcm,nand-bcm63168
|
||||
- const: brcm,nand-bcm6368
|
||||
- const: brcm,brcmnand-v4.0
|
||||
- const: brcm,brcmnand
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 6
|
||||
|
||||
reg-names:
|
||||
minItems: 1
|
||||
maxItems: 6
|
||||
items:
|
||||
enum: [ nand, flash-dma, flash-edu, nand-cache, nand-int-base, iproc-idm, iproc-ext ]
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
items:
|
||||
- description: NAND CTLRDY interrupt
|
||||
- description: FLASH_DMA_DONE if flash DMA is available
|
||||
- description: FLASH_EDU_DONE if EDU is available
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
items:
|
||||
- const: nand_ctlrdy
|
||||
- const: flash_dma_done
|
||||
- const: flash_edu_done
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: reference to the clock for the NAND controller
|
||||
|
||||
clock-names:
|
||||
const: nand
|
||||
|
||||
brcm,nand-has-wp:
|
||||
description: >
|
||||
Some versions of this IP include a write-protect
|
||||
(WP) control bit. It is always available on >=
|
||||
v7.0. Use this property to describe the rare
|
||||
earlier versions of this core that include WP
|
||||
type: boolean
|
||||
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]$":
|
||||
type: object
|
||||
properties:
|
||||
compatible:
|
||||
const: brcm,nandcs
|
||||
|
||||
nand-ecc-step-size:
|
||||
enum: [ 512, 1024 ]
|
||||
|
||||
brcm,nand-oob-sector-size:
|
||||
description: |
|
||||
integer, to denote the spare area sector size
|
||||
expected for the ECC layout in use. This size, in
|
||||
addition to the strength and step-size,
|
||||
determines how the hardware BCH engine will lay
|
||||
out the parity bytes it stores on the flash.
|
||||
This property can be automatically determined by
|
||||
the flash geometry (particularly the NAND page
|
||||
and OOB size) in many cases, but when booting
|
||||
from NAND, the boot controller has only a limited
|
||||
number of available options for its default ECC
|
||||
layout.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
allOf:
|
||||
- $ref: nand-controller.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: brcm,nand-bcm63138
|
||||
then:
|
||||
properties:
|
||||
reg-names:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
items:
|
||||
- const: nand
|
||||
- const: nand-int-base
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: brcm,nand-bcm6368
|
||||
then:
|
||||
properties:
|
||||
reg-names:
|
||||
minItems: 3
|
||||
maxItems: 3
|
||||
items:
|
||||
- const: nand
|
||||
- const: nand-int-base
|
||||
- const: nand-cache
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: brcm,nand-iproc
|
||||
then:
|
||||
properties:
|
||||
reg-names:
|
||||
minItems: 3
|
||||
maxItems: 3
|
||||
items:
|
||||
- const: nand
|
||||
- const: iproc-idm
|
||||
- const: iproc-ext
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
nand-controller@f0442800 {
|
||||
compatible = "brcm,brcmnand-v7.0", "brcm,brcmnand";
|
||||
reg = <0xf0442800 0x600>,
|
||||
<0xf0443000 0x100>;
|
||||
reg-names = "nand", "flash-dma";
|
||||
interrupt-parent = <&hif_intr2_intc>;
|
||||
interrupts = <24>, <4>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@1 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <1>; // Chip select 1
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-strength = <12>;
|
||||
nand-ecc-step-size = <512>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
nand-controller@10000200 {
|
||||
compatible = "brcm,nand-bcm63168", "brcm,nand-bcm6368",
|
||||
"brcm,brcmnand-v4.0", "brcm,brcmnand";
|
||||
reg = <0x10000200 0x180>,
|
||||
<0x100000b0 0x10>,
|
||||
<0x10000600 0x200>;
|
||||
reg-names = "nand", "nand-int-base", "nand-cache";
|
||||
interrupt-parent = <&periph_intc>;
|
||||
interrupts = <50>;
|
||||
clocks = <&periph_clk 20>;
|
||||
clock-names = "nand";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-strength = <1>;
|
||||
nand-ecc-step-size = <512>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
};
|
@ -38,6 +38,17 @@ properties:
|
||||
|
||||
ranges: true
|
||||
|
||||
cs-gpios:
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
description:
|
||||
Array of chip-select available to the controller. The first
|
||||
entries are a 1:1 mapping of the available chip-select on the
|
||||
NAND controller (even if they are not used). As many additional
|
||||
chip-select as needed may follow and should be phandles of GPIO
|
||||
lines. 'reg' entries of the NAND chip subnodes become indexes of
|
||||
this array when this property is present.
|
||||
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]$":
|
||||
type: object
|
||||
@ -164,14 +175,19 @@ examples:
|
||||
nand-controller {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
cs-gpios = <0>, <&gpioA 1>; /* A single native CS is available */
|
||||
|
||||
/* controller specific properties */
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
reg = <0>; /* Native CS */
|
||||
nand-use-soft-ecc-engine;
|
||||
nand-ecc-algo = "bch";
|
||||
|
||||
/* controller specific properties */
|
||||
};
|
||||
|
||||
nand@1 {
|
||||
reg = <1>; /* GPIO CS */
|
||||
};
|
||||
};
|
||||
|
17
MAINTAINERS
17
MAINTAINERS
@ -1311,6 +1311,7 @@ W: http://www.aquantia.com
|
||||
F: drivers/net/ethernet/aquantia/atlantic/aq_ptp*
|
||||
|
||||
ARASAN NAND CONTROLLER DRIVER
|
||||
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
M: Naga Sureshkumar Relli <nagasure@xilinx.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
S: Maintained
|
||||
@ -1452,6 +1453,22 @@ S: Odd Fixes
|
||||
F: drivers/amba/
|
||||
F: include/linux/amba/bus.h
|
||||
|
||||
ARM PRIMECELL PL35X NAND CONTROLLER DRIVER
|
||||
M: Miquel Raynal <miquel.raynal@bootlin.com@bootlin.com>
|
||||
M: Naga Sureshkumar Relli <nagasure@xilinx.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/mtd/arm,pl353-nand-r2p1.yaml
|
||||
F: drivers/mtd/nand/raw/pl35x-nand-controller.c
|
||||
|
||||
ARM PRIMECELL PL35X SMC DRIVER
|
||||
M: Miquel Raynal <miquel.raynal@bootlin.com@bootlin.com>
|
||||
M: Naga Sureshkumar Relli <nagasure@xilinx.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/mtd/arm,pl353-smc.yaml
|
||||
F: drivers/memory/pl353-smc.c
|
||||
|
||||
ARM PRIMECELL CLCD PL110 DRIVER
|
||||
M: Russell King <linux@armlinux.org.uk>
|
||||
S: Odd Fixes
|
||||
|
@ -8,263 +8,22 @@
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pl353-smc.h>
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
/* Register definitions */
|
||||
#define PL353_SMC_MEMC_STATUS_OFFS 0 /* Controller status reg, RO */
|
||||
#define PL353_SMC_CFG_CLR_OFFS 0xC /* Clear config reg, WO */
|
||||
#define PL353_SMC_DIRECT_CMD_OFFS 0x10 /* Direct command reg, WO */
|
||||
#define PL353_SMC_SET_CYCLES_OFFS 0x14 /* Set cycles register, WO */
|
||||
#define PL353_SMC_SET_OPMODE_OFFS 0x18 /* Set opmode register, WO */
|
||||
#define PL353_SMC_ECC_STATUS_OFFS 0x400 /* ECC status register */
|
||||
#define PL353_SMC_ECC_MEMCFG_OFFS 0x404 /* ECC mem config reg */
|
||||
#define PL353_SMC_ECC_MEMCMD1_OFFS 0x408 /* ECC mem cmd1 reg */
|
||||
#define PL353_SMC_ECC_MEMCMD2_OFFS 0x40C /* ECC mem cmd2 reg */
|
||||
#define PL353_SMC_ECC_VALUE0_OFFS 0x418 /* ECC value 0 reg */
|
||||
|
||||
/* Controller status register specific constants */
|
||||
#define PL353_SMC_MEMC_STATUS_RAW_INT_1_SHIFT 6
|
||||
|
||||
/* Clear configuration register specific constants */
|
||||
#define PL353_SMC_CFG_CLR_INT_CLR_1 0x10
|
||||
#define PL353_SMC_CFG_CLR_ECC_INT_DIS_1 0x40
|
||||
#define PL353_SMC_CFG_CLR_INT_DIS_1 0x2
|
||||
#define PL353_SMC_CFG_CLR_DEFAULT_MASK (PL353_SMC_CFG_CLR_INT_CLR_1 | \
|
||||
PL353_SMC_CFG_CLR_ECC_INT_DIS_1 | \
|
||||
PL353_SMC_CFG_CLR_INT_DIS_1)
|
||||
|
||||
/* Set cycles register specific constants */
|
||||
#define PL353_SMC_SET_CYCLES_T0_MASK 0xF
|
||||
#define PL353_SMC_SET_CYCLES_T0_SHIFT 0
|
||||
#define PL353_SMC_SET_CYCLES_T1_MASK 0xF
|
||||
#define PL353_SMC_SET_CYCLES_T1_SHIFT 4
|
||||
#define PL353_SMC_SET_CYCLES_T2_MASK 0x7
|
||||
#define PL353_SMC_SET_CYCLES_T2_SHIFT 8
|
||||
#define PL353_SMC_SET_CYCLES_T3_MASK 0x7
|
||||
#define PL353_SMC_SET_CYCLES_T3_SHIFT 11
|
||||
#define PL353_SMC_SET_CYCLES_T4_MASK 0x7
|
||||
#define PL353_SMC_SET_CYCLES_T4_SHIFT 14
|
||||
#define PL353_SMC_SET_CYCLES_T5_MASK 0x7
|
||||
#define PL353_SMC_SET_CYCLES_T5_SHIFT 17
|
||||
#define PL353_SMC_SET_CYCLES_T6_MASK 0xF
|
||||
#define PL353_SMC_SET_CYCLES_T6_SHIFT 20
|
||||
|
||||
/* ECC status register specific constants */
|
||||
#define PL353_SMC_ECC_STATUS_BUSY BIT(6)
|
||||
#define PL353_SMC_ECC_REG_SIZE_OFFS 4
|
||||
|
||||
/* ECC memory config register specific constants */
|
||||
#define PL353_SMC_ECC_MEMCFG_MODE_MASK 0xC
|
||||
#define PL353_SMC_ECC_MEMCFG_MODE_SHIFT 2
|
||||
#define PL353_SMC_ECC_MEMCFG_PGSIZE_MASK 0x3
|
||||
|
||||
#define PL353_SMC_DC_UPT_NAND_REGS ((4 << 23) | /* CS: NAND chip */ \
|
||||
(2 << 21)) /* UpdateRegs operation */
|
||||
|
||||
#define PL353_NAND_ECC_CMD1 ((0x80) | /* Write command */ \
|
||||
(0 << 8) | /* Read command */ \
|
||||
(0x30 << 16) | /* Read End command */ \
|
||||
(1 << 24)) /* Read End command calid */
|
||||
|
||||
#define PL353_NAND_ECC_CMD2 ((0x85) | /* Write col change cmd */ \
|
||||
(5 << 8) | /* Read col change cmd */ \
|
||||
(0xE0 << 16) | /* Read col change end cmd */ \
|
||||
(1 << 24)) /* Read col change end cmd valid */
|
||||
#define PL353_NAND_ECC_BUSY_TIMEOUT (1 * HZ)
|
||||
/**
|
||||
* struct pl353_smc_data - Private smc driver structure
|
||||
* @memclk: Pointer to the peripheral clock
|
||||
* @aclk: Pointer to the APER clock
|
||||
* @aclk: Pointer to the AXI peripheral clock
|
||||
*/
|
||||
struct pl353_smc_data {
|
||||
struct clk *memclk;
|
||||
struct clk *aclk;
|
||||
};
|
||||
|
||||
/* SMC virtual register base */
|
||||
static void __iomem *pl353_smc_base;
|
||||
|
||||
/**
|
||||
* pl353_smc_set_buswidth - Set memory buswidth
|
||||
* @bw: Memory buswidth (8 | 16)
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
int pl353_smc_set_buswidth(unsigned int bw)
|
||||
{
|
||||
if (bw != PL353_SMC_MEM_WIDTH_8 && bw != PL353_SMC_MEM_WIDTH_16)
|
||||
return -EINVAL;
|
||||
|
||||
writel(bw, pl353_smc_base + PL353_SMC_SET_OPMODE_OFFS);
|
||||
writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base +
|
||||
PL353_SMC_DIRECT_CMD_OFFS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pl353_smc_set_buswidth);
|
||||
|
||||
/**
|
||||
* pl353_smc_set_cycles - Set memory timing parameters
|
||||
* @timings: NAND controller timing parameters
|
||||
*
|
||||
* Sets NAND chip specific timing parameters.
|
||||
*/
|
||||
void pl353_smc_set_cycles(u32 timings[])
|
||||
{
|
||||
/*
|
||||
* Set write pulse timing. This one is easy to extract:
|
||||
*
|
||||
* NWE_PULSE = tWP
|
||||
*/
|
||||
timings[0] &= PL353_SMC_SET_CYCLES_T0_MASK;
|
||||
timings[1] = (timings[1] & PL353_SMC_SET_CYCLES_T1_MASK) <<
|
||||
PL353_SMC_SET_CYCLES_T1_SHIFT;
|
||||
timings[2] = (timings[2] & PL353_SMC_SET_CYCLES_T2_MASK) <<
|
||||
PL353_SMC_SET_CYCLES_T2_SHIFT;
|
||||
timings[3] = (timings[3] & PL353_SMC_SET_CYCLES_T3_MASK) <<
|
||||
PL353_SMC_SET_CYCLES_T3_SHIFT;
|
||||
timings[4] = (timings[4] & PL353_SMC_SET_CYCLES_T4_MASK) <<
|
||||
PL353_SMC_SET_CYCLES_T4_SHIFT;
|
||||
timings[5] = (timings[5] & PL353_SMC_SET_CYCLES_T5_MASK) <<
|
||||
PL353_SMC_SET_CYCLES_T5_SHIFT;
|
||||
timings[6] = (timings[6] & PL353_SMC_SET_CYCLES_T6_MASK) <<
|
||||
PL353_SMC_SET_CYCLES_T6_SHIFT;
|
||||
timings[0] |= timings[1] | timings[2] | timings[3] |
|
||||
timings[4] | timings[5] | timings[6];
|
||||
|
||||
writel(timings[0], pl353_smc_base + PL353_SMC_SET_CYCLES_OFFS);
|
||||
writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base +
|
||||
PL353_SMC_DIRECT_CMD_OFFS);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pl353_smc_set_cycles);
|
||||
|
||||
/**
|
||||
* pl353_smc_ecc_is_busy - Read ecc busy flag
|
||||
* Return: the ecc_status bit from the ecc_status register. 1 = busy, 0 = idle
|
||||
*/
|
||||
bool pl353_smc_ecc_is_busy(void)
|
||||
{
|
||||
return ((readl(pl353_smc_base + PL353_SMC_ECC_STATUS_OFFS) &
|
||||
PL353_SMC_ECC_STATUS_BUSY) == PL353_SMC_ECC_STATUS_BUSY);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pl353_smc_ecc_is_busy);
|
||||
|
||||
/**
|
||||
* pl353_smc_get_ecc_val - Read ecc_valueN registers
|
||||
* @ecc_reg: Index of the ecc_value reg (0..3)
|
||||
* Return: the content of the requested ecc_value register.
|
||||
*
|
||||
* There are four valid ecc_value registers. The argument is truncated to stay
|
||||
* within this valid boundary.
|
||||
*/
|
||||
u32 pl353_smc_get_ecc_val(int ecc_reg)
|
||||
{
|
||||
u32 addr, reg;
|
||||
|
||||
addr = PL353_SMC_ECC_VALUE0_OFFS +
|
||||
(ecc_reg * PL353_SMC_ECC_REG_SIZE_OFFS);
|
||||
reg = readl(pl353_smc_base + addr);
|
||||
|
||||
return reg;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pl353_smc_get_ecc_val);
|
||||
|
||||
/**
|
||||
* pl353_smc_get_nand_int_status_raw - Get NAND interrupt status bit
|
||||
* Return: the raw_int_status1 bit from the memc_status register
|
||||
*/
|
||||
int pl353_smc_get_nand_int_status_raw(void)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(pl353_smc_base + PL353_SMC_MEMC_STATUS_OFFS);
|
||||
reg >>= PL353_SMC_MEMC_STATUS_RAW_INT_1_SHIFT;
|
||||
reg &= 1;
|
||||
|
||||
return reg;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pl353_smc_get_nand_int_status_raw);
|
||||
|
||||
/**
|
||||
* pl353_smc_clr_nand_int - Clear NAND interrupt
|
||||
*/
|
||||
void pl353_smc_clr_nand_int(void)
|
||||
{
|
||||
writel(PL353_SMC_CFG_CLR_INT_CLR_1,
|
||||
pl353_smc_base + PL353_SMC_CFG_CLR_OFFS);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pl353_smc_clr_nand_int);
|
||||
|
||||
/**
|
||||
* pl353_smc_set_ecc_mode - Set SMC ECC mode
|
||||
* @mode: ECC mode (BYPASS, APB, MEM)
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
int pl353_smc_set_ecc_mode(enum pl353_smc_ecc_mode mode)
|
||||
{
|
||||
u32 reg;
|
||||
int ret = 0;
|
||||
|
||||
switch (mode) {
|
||||
case PL353_SMC_ECCMODE_BYPASS:
|
||||
case PL353_SMC_ECCMODE_APB:
|
||||
case PL353_SMC_ECCMODE_MEM:
|
||||
|
||||
reg = readl(pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS);
|
||||
reg &= ~PL353_SMC_ECC_MEMCFG_MODE_MASK;
|
||||
reg |= mode << PL353_SMC_ECC_MEMCFG_MODE_SHIFT;
|
||||
writel(reg, pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS);
|
||||
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pl353_smc_set_ecc_mode);
|
||||
|
||||
/**
|
||||
* pl353_smc_set_ecc_pg_size - Set SMC ECC page size
|
||||
* @pg_sz: ECC page size
|
||||
* Return: 0 on success or negative errno.
|
||||
*/
|
||||
int pl353_smc_set_ecc_pg_size(unsigned int pg_sz)
|
||||
{
|
||||
u32 reg, sz;
|
||||
|
||||
switch (pg_sz) {
|
||||
case 0:
|
||||
sz = 0;
|
||||
break;
|
||||
case SZ_512:
|
||||
sz = 1;
|
||||
break;
|
||||
case SZ_1K:
|
||||
sz = 2;
|
||||
break;
|
||||
case SZ_2K:
|
||||
sz = 3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = readl(pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS);
|
||||
reg &= ~PL353_SMC_ECC_MEMCFG_PGSIZE_MASK;
|
||||
reg |= sz;
|
||||
writel(reg, pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pl353_smc_set_ecc_pg_size);
|
||||
|
||||
static int __maybe_unused pl353_smc_suspend(struct device *dev)
|
||||
{
|
||||
struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev);
|
||||
@ -277,8 +36,8 @@ static int __maybe_unused pl353_smc_suspend(struct device *dev)
|
||||
|
||||
static int __maybe_unused pl353_smc_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(pl353_smc->aclk);
|
||||
if (ret) {
|
||||
@ -296,77 +55,31 @@ static int __maybe_unused pl353_smc_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct amba_driver pl353_smc_driver;
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pl353_smc_dev_pm_ops, pl353_smc_suspend,
|
||||
pl353_smc_resume);
|
||||
|
||||
/**
|
||||
* pl353_smc_init_nand_interface - Initialize the NAND interface
|
||||
* @adev: Pointer to the amba_device struct
|
||||
* @nand_node: Pointer to the pl353_nand device_node struct
|
||||
*/
|
||||
static void pl353_smc_init_nand_interface(struct amba_device *adev,
|
||||
struct device_node *nand_node)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
pl353_smc_set_buswidth(PL353_SMC_MEM_WIDTH_8);
|
||||
writel(PL353_SMC_CFG_CLR_INT_CLR_1,
|
||||
pl353_smc_base + PL353_SMC_CFG_CLR_OFFS);
|
||||
writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base +
|
||||
PL353_SMC_DIRECT_CMD_OFFS);
|
||||
|
||||
timeout = jiffies + PL353_NAND_ECC_BUSY_TIMEOUT;
|
||||
/* Wait till the ECC operation is complete */
|
||||
do {
|
||||
if (pl353_smc_ecc_is_busy())
|
||||
cpu_relax();
|
||||
else
|
||||
break;
|
||||
} while (!time_after_eq(jiffies, timeout));
|
||||
|
||||
if (time_after_eq(jiffies, timeout))
|
||||
return;
|
||||
|
||||
writel(PL353_NAND_ECC_CMD1,
|
||||
pl353_smc_base + PL353_SMC_ECC_MEMCMD1_OFFS);
|
||||
writel(PL353_NAND_ECC_CMD2,
|
||||
pl353_smc_base + PL353_SMC_ECC_MEMCMD2_OFFS);
|
||||
}
|
||||
|
||||
static const struct of_device_id pl353_smc_supported_children[] = {
|
||||
{
|
||||
.compatible = "cfi-flash"
|
||||
},
|
||||
{
|
||||
.compatible = "arm,pl353-nand-r2p1",
|
||||
.data = pl353_smc_init_nand_interface
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
struct device_node *of_node = adev->dev.of_node;
|
||||
const struct of_device_id *match = NULL;
|
||||
struct pl353_smc_data *pl353_smc;
|
||||
struct device_node *child;
|
||||
struct resource *res;
|
||||
int err;
|
||||
struct device_node *of_node = adev->dev.of_node;
|
||||
static void (*init)(struct amba_device *adev,
|
||||
struct device_node *nand_node);
|
||||
const struct of_device_id *match = NULL;
|
||||
|
||||
pl353_smc = devm_kzalloc(&adev->dev, sizeof(*pl353_smc), GFP_KERNEL);
|
||||
if (!pl353_smc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get the NAND controller virtual address */
|
||||
res = &adev->res;
|
||||
pl353_smc_base = devm_ioremap_resource(&adev->dev, res);
|
||||
if (IS_ERR(pl353_smc_base))
|
||||
return PTR_ERR(pl353_smc_base);
|
||||
|
||||
pl353_smc->aclk = devm_clk_get(&adev->dev, "apb_pclk");
|
||||
if (IS_ERR(pl353_smc->aclk)) {
|
||||
dev_err(&adev->dev, "aclk clock not found.\n");
|
||||
@ -388,15 +101,11 @@ static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
err = clk_prepare_enable(pl353_smc->memclk);
|
||||
if (err) {
|
||||
dev_err(&adev->dev, "Unable to enable memory clock.\n");
|
||||
goto out_clk_dis_aper;
|
||||
goto disable_axi_clk;
|
||||
}
|
||||
|
||||
amba_set_drvdata(adev, pl353_smc);
|
||||
|
||||
/* clear interrupts */
|
||||
writel(PL353_SMC_CFG_CLR_DEFAULT_MASK,
|
||||
pl353_smc_base + PL353_SMC_CFG_CLR_OFFS);
|
||||
|
||||
/* Find compatible children. Only a single child is supported */
|
||||
for_each_available_child_of_node(of_node, child) {
|
||||
match = of_match_node(pl353_smc_supported_children, child);
|
||||
@ -408,19 +117,16 @@ static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
}
|
||||
if (!match) {
|
||||
dev_err(&adev->dev, "no matching children\n");
|
||||
goto out_clk_disable;
|
||||
goto disable_mem_clk;
|
||||
}
|
||||
|
||||
init = match->data;
|
||||
if (init)
|
||||
init(adev, child);
|
||||
of_platform_device_create(child, NULL, &adev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
out_clk_disable:
|
||||
disable_mem_clk:
|
||||
clk_disable_unprepare(pl353_smc->memclk);
|
||||
out_clk_dis_aper:
|
||||
disable_axi_clk:
|
||||
clk_disable_unprepare(pl353_smc->aclk);
|
||||
|
||||
return err;
|
||||
@ -436,8 +142,8 @@ static void pl353_smc_remove(struct amba_device *adev)
|
||||
|
||||
static const struct amba_id pl353_ids[] = {
|
||||
{
|
||||
.id = 0x00041353,
|
||||
.mask = 0x000fffff,
|
||||
.id = 0x00041353,
|
||||
.mask = 0x000fffff,
|
||||
},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
@ -123,7 +123,7 @@ int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
|
||||
unsigned int rbits = bits_per_block + offs - BITS_PER_LONG;
|
||||
|
||||
pos[1] &= ~GENMASK(rbits - 1, 0);
|
||||
pos[1] |= val >> rbits;
|
||||
pos[1] |= val >> (bits_per_block - rbits);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -453,6 +453,14 @@ config MTD_NAND_ROCKCHIP
|
||||
NFC v800: RK3308, RV1108
|
||||
NFC v900: PX30, RK3326
|
||||
|
||||
config MTD_NAND_PL35X
|
||||
tristate "ARM PL35X NAND controller"
|
||||
depends on OF || COMPILE_TEST
|
||||
depends on PL353_SMC
|
||||
help
|
||||
Enables support for PrimeCell SMC PL351 and PL353 NAND
|
||||
controller found on Zynq7000.
|
||||
|
||||
comment "Misc"
|
||||
|
||||
config MTD_SM_COMMON
|
||||
|
@ -57,6 +57,7 @@ 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
|
||||
obj-$(CONFIG_MTD_NAND_PL35X) += pl35x-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
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
@ -53,6 +54,7 @@
|
||||
#define PROG_RST BIT(8)
|
||||
#define PROG_GET_FEATURE BIT(9)
|
||||
#define PROG_SET_FEATURE BIT(10)
|
||||
#define PROG_CHG_RD_COL_ENH BIT(14)
|
||||
|
||||
#define INTR_STS_EN_REG 0x14
|
||||
#define INTR_SIG_EN_REG 0x18
|
||||
@ -70,6 +72,15 @@
|
||||
|
||||
#define FLASH_STS_REG 0x28
|
||||
|
||||
#define TIMING_REG 0x2C
|
||||
#define TCCS_TIME_500NS 0
|
||||
#define TCCS_TIME_300NS 3
|
||||
#define TCCS_TIME_200NS 2
|
||||
#define TCCS_TIME_100NS 1
|
||||
#define FAST_TCAD BIT(2)
|
||||
#define DQS_BUFF_SEL_IN(x) FIELD_PREP(GENMASK(6, 3), (x))
|
||||
#define DQS_BUFF_SEL_OUT(x) FIELD_PREP(GENMASK(18, 15), (x))
|
||||
|
||||
#define DATA_PORT_REG 0x30
|
||||
|
||||
#define ECC_CONF_REG 0x34
|
||||
@ -91,7 +102,7 @@
|
||||
|
||||
#define DATA_INTERFACE_REG 0x6C
|
||||
#define DIFACE_SDR_MODE(x) FIELD_PREP(GENMASK(2, 0), (x))
|
||||
#define DIFACE_DDR_MODE(x) FIELD_PREP(GENMASK(5, 3), (X))
|
||||
#define DIFACE_DDR_MODE(x) FIELD_PREP(GENMASK(5, 3), (x))
|
||||
#define DIFACE_SDR 0
|
||||
#define DIFACE_NVDDR BIT(9)
|
||||
|
||||
@ -107,6 +118,8 @@
|
||||
#define ANFC_XLNX_SDR_DFLT_CORE_CLK 100000000
|
||||
#define ANFC_XLNX_SDR_HS_CORE_CLK 80000000
|
||||
|
||||
static struct gpio_desc *anfc_default_cs_array[2] = {NULL, NULL};
|
||||
|
||||
/**
|
||||
* struct anfc_op - Defines how to execute an operation
|
||||
* @pkt_reg: Packet register
|
||||
@ -137,11 +150,11 @@ struct anfc_op {
|
||||
* struct anand - Defines the NAND chip related information
|
||||
* @node: Used to store NAND chips into a list
|
||||
* @chip: NAND chip information structure
|
||||
* @cs: Chip select line
|
||||
* @rb: Ready-busy line
|
||||
* @page_sz: Register value of the page_sz field to use
|
||||
* @clk: Expected clock frequency to use
|
||||
* @timings: Data interface timing mode to use
|
||||
* @data_iface: Data interface timing mode to use
|
||||
* @timings: NV-DDR specific timings to use
|
||||
* @ecc_conf: Hardware ECC configuration value
|
||||
* @strength: Register value of the ECC strength
|
||||
* @raddr_cycles: Row address cycle information
|
||||
@ -151,14 +164,17 @@ struct anfc_op {
|
||||
* @errloc: Array of errors located with soft BCH
|
||||
* @hw_ecc: Buffer to store syndromes computed by hardware
|
||||
* @bch: BCH structure
|
||||
* @cs_idx: Array of chip-select for this device, values are indexes
|
||||
* of the controller structure @gpio_cs array
|
||||
* @ncs_idx: Size of the @cs_idx array
|
||||
*/
|
||||
struct anand {
|
||||
struct list_head node;
|
||||
struct nand_chip chip;
|
||||
unsigned int cs;
|
||||
unsigned int rb;
|
||||
unsigned int page_sz;
|
||||
unsigned long clk;
|
||||
u32 data_iface;
|
||||
u32 timings;
|
||||
u32 ecc_conf;
|
||||
u32 strength;
|
||||
@ -169,6 +185,8 @@ struct anand {
|
||||
unsigned int *errloc;
|
||||
u8 *hw_ecc;
|
||||
struct bch_control *bch;
|
||||
int *cs_idx;
|
||||
int ncs_idx;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -179,8 +197,14 @@ struct anand {
|
||||
* @bus_clk: Pointer to the flash clock
|
||||
* @controller: Base controller structure
|
||||
* @chips: List of all NAND chips attached to the controller
|
||||
* @assigned_cs: Bitmask describing already assigned CS lines
|
||||
* @cur_clk: Current clock rate
|
||||
* @cs_array: CS array. Native CS are left empty, the other cells are
|
||||
* populated with their corresponding GPIO descriptor.
|
||||
* @ncs: Size of @cs_array
|
||||
* @cur_cs: Index in @cs_array of the currently in use CS
|
||||
* @native_cs: Currently selected native CS
|
||||
* @spare_cs: Native CS that is not wired (may be selected when a GPIO
|
||||
* CS is in use)
|
||||
*/
|
||||
struct arasan_nfc {
|
||||
struct device *dev;
|
||||
@ -189,8 +213,12 @@ struct arasan_nfc {
|
||||
struct clk *bus_clk;
|
||||
struct nand_controller controller;
|
||||
struct list_head chips;
|
||||
unsigned long assigned_cs;
|
||||
unsigned int cur_clk;
|
||||
struct gpio_desc **cs_array;
|
||||
unsigned int ncs;
|
||||
int cur_cs;
|
||||
unsigned int native_cs;
|
||||
unsigned int spare_cs;
|
||||
};
|
||||
|
||||
static struct anand *to_anand(struct nand_chip *nand)
|
||||
@ -273,6 +301,72 @@ static int anfc_pkt_len_config(unsigned int len, unsigned int *steps,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool anfc_is_gpio_cs(struct arasan_nfc *nfc, int nfc_cs)
|
||||
{
|
||||
return nfc_cs >= 0 && nfc->cs_array[nfc_cs];
|
||||
}
|
||||
|
||||
static int anfc_relative_to_absolute_cs(struct anand *anand, int num)
|
||||
{
|
||||
return anand->cs_idx[num];
|
||||
}
|
||||
|
||||
static void anfc_assert_cs(struct arasan_nfc *nfc, unsigned int nfc_cs_idx)
|
||||
{
|
||||
/* CS did not change: do nothing */
|
||||
if (nfc->cur_cs == nfc_cs_idx)
|
||||
return;
|
||||
|
||||
/* Deassert the previous CS if it was a GPIO */
|
||||
if (anfc_is_gpio_cs(nfc, nfc->cur_cs))
|
||||
gpiod_set_value_cansleep(nfc->cs_array[nfc->cur_cs], 1);
|
||||
|
||||
/* Assert the new one */
|
||||
if (anfc_is_gpio_cs(nfc, nfc_cs_idx)) {
|
||||
nfc->native_cs = nfc->spare_cs;
|
||||
gpiod_set_value_cansleep(nfc->cs_array[nfc_cs_idx], 0);
|
||||
} else {
|
||||
nfc->native_cs = nfc_cs_idx;
|
||||
}
|
||||
|
||||
nfc->cur_cs = nfc_cs_idx;
|
||||
}
|
||||
|
||||
static int anfc_select_target(struct nand_chip *chip, int target)
|
||||
{
|
||||
struct anand *anand = to_anand(chip);
|
||||
struct arasan_nfc *nfc = to_anfc(chip->controller);
|
||||
unsigned int nfc_cs_idx = anfc_relative_to_absolute_cs(anand, target);
|
||||
int ret;
|
||||
|
||||
anfc_assert_cs(nfc, nfc_cs_idx);
|
||||
|
||||
/* Update the controller timings and the potential ECC configuration */
|
||||
writel_relaxed(anand->data_iface, nfc->base + DATA_INTERFACE_REG);
|
||||
writel_relaxed(anand->timings, nfc->base + TIMING_REG);
|
||||
|
||||
/* Update clock frequency */
|
||||
if (nfc->cur_clk != anand->clk) {
|
||||
clk_disable_unprepare(nfc->controller_clk);
|
||||
ret = clk_set_rate(nfc->controller_clk, anand->clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "Failed to change clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(nfc->controller_clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev,
|
||||
"Failed to re-enable the controller clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
nfc->cur_clk = anand->clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When using the embedded hardware ECC engine, the controller is in charge of
|
||||
* feeding the engine with, first, the ECC residue present in the data array.
|
||||
@ -315,7 +409,7 @@ static int anfc_read_page_hw_ecc(struct nand_chip *chip, u8 *buf,
|
||||
.addr2_reg =
|
||||
((page >> 16) & 0xFF) |
|
||||
ADDR2_STRENGTH(anand->strength) |
|
||||
ADDR2_CS(anand->cs),
|
||||
ADDR2_CS(nfc->native_cs),
|
||||
.cmd_reg =
|
||||
CMD_1(NAND_CMD_READ0) |
|
||||
CMD_2(NAND_CMD_READSTART) |
|
||||
@ -401,6 +495,18 @@ static int anfc_read_page_hw_ecc(struct nand_chip *chip, u8 *buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int anfc_sel_read_page_hw_ecc(struct nand_chip *chip, u8 *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = anfc_select_target(chip, chip->cur_cs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return anfc_read_page_hw_ecc(chip, buf, oob_required, page);
|
||||
};
|
||||
|
||||
static int anfc_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
@ -420,7 +526,7 @@ static int anfc_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf,
|
||||
.addr2_reg =
|
||||
((page >> 16) & 0xFF) |
|
||||
ADDR2_STRENGTH(anand->strength) |
|
||||
ADDR2_CS(anand->cs),
|
||||
ADDR2_CS(nfc->native_cs),
|
||||
.cmd_reg =
|
||||
CMD_1(NAND_CMD_SEQIN) |
|
||||
CMD_2(NAND_CMD_PAGEPROG) |
|
||||
@ -461,11 +567,24 @@ static int anfc_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int anfc_sel_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = anfc_select_target(chip, chip->cur_cs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return anfc_write_page_hw_ecc(chip, buf, oob_required, page);
|
||||
};
|
||||
|
||||
/* NAND framework ->exec_op() hooks and related helpers */
|
||||
static int anfc_parse_instructions(struct nand_chip *chip,
|
||||
const struct nand_subop *subop,
|
||||
struct anfc_op *nfc_op)
|
||||
{
|
||||
struct arasan_nfc *nfc = to_anfc(chip->controller);
|
||||
struct anand *anand = to_anand(chip);
|
||||
const struct nand_op_instr *instr = NULL;
|
||||
bool first_cmd = true;
|
||||
@ -473,7 +592,7 @@ static int anfc_parse_instructions(struct nand_chip *chip,
|
||||
int ret, i;
|
||||
|
||||
memset(nfc_op, 0, sizeof(*nfc_op));
|
||||
nfc_op->addr2_reg = ADDR2_CS(anand->cs);
|
||||
nfc_op->addr2_reg = ADDR2_CS(nfc->native_cs);
|
||||
nfc_op->cmd_reg = CMD_PAGE_SIZE(anand->page_sz);
|
||||
|
||||
for (op_id = 0; op_id < subop->ninstrs; op_id++) {
|
||||
@ -622,7 +741,23 @@ static int anfc_param_read_type_exec(struct nand_chip *chip,
|
||||
static int anfc_data_read_type_exec(struct nand_chip *chip,
|
||||
const struct nand_subop *subop)
|
||||
{
|
||||
return anfc_misc_data_type_exec(chip, subop, PROG_PGRD);
|
||||
u32 prog_reg = PROG_PGRD;
|
||||
|
||||
/*
|
||||
* Experience shows that while in SDR mode sending a CHANGE READ COLUMN
|
||||
* command through the READ PAGE "type" always works fine, when in
|
||||
* NV-DDR mode the same command simply fails. However, it was also
|
||||
* spotted that any CHANGE READ COLUMN command sent through the CHANGE
|
||||
* READ COLUMN ENHANCED "type" would correctly work in both cases (SDR
|
||||
* and NV-DDR). So, for simplicity, let's program the controller with
|
||||
* the CHANGE READ COLUMN ENHANCED "type" whenever we are requested to
|
||||
* perform a CHANGE READ COLUMN operation.
|
||||
*/
|
||||
if (subop->instrs[0].ctx.cmd.opcode == NAND_CMD_RNDOUT &&
|
||||
subop->instrs[2].ctx.cmd.opcode == NAND_CMD_RNDOUTSTART)
|
||||
prog_reg = PROG_CHG_RD_COL_ENH;
|
||||
|
||||
return anfc_misc_data_type_exec(chip, subop, prog_reg);
|
||||
}
|
||||
|
||||
static int anfc_param_write_type_exec(struct nand_chip *chip,
|
||||
@ -753,37 +888,6 @@ static const struct nand_op_parser anfc_op_parser = NAND_OP_PARSER(
|
||||
NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
|
||||
);
|
||||
|
||||
static int anfc_select_target(struct nand_chip *chip, int target)
|
||||
{
|
||||
struct anand *anand = to_anand(chip);
|
||||
struct arasan_nfc *nfc = to_anfc(chip->controller);
|
||||
int ret;
|
||||
|
||||
/* Update the controller timings and the potential ECC configuration */
|
||||
writel_relaxed(anand->timings, nfc->base + DATA_INTERFACE_REG);
|
||||
|
||||
/* Update clock frequency */
|
||||
if (nfc->cur_clk != anand->clk) {
|
||||
clk_disable_unprepare(nfc->controller_clk);
|
||||
ret = clk_set_rate(nfc->controller_clk, anand->clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "Failed to change clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(nfc->controller_clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev,
|
||||
"Failed to re-enable the controller clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
nfc->cur_clk = anand->clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int anfc_check_op(struct nand_chip *chip,
|
||||
const struct nand_operation *op)
|
||||
{
|
||||
@ -861,21 +965,79 @@ static int anfc_setup_interface(struct nand_chip *chip, int target,
|
||||
struct anand *anand = to_anand(chip);
|
||||
struct arasan_nfc *nfc = to_anfc(chip->controller);
|
||||
struct device_node *np = nfc->dev->of_node;
|
||||
const struct nand_sdr_timings *sdr;
|
||||
const struct nand_nvddr_timings *nvddr;
|
||||
unsigned int tccs_min, dqs_mode, fast_tcad;
|
||||
|
||||
if (nand_interface_is_nvddr(conf)) {
|
||||
nvddr = nand_get_nvddr_timings(conf);
|
||||
if (IS_ERR(nvddr))
|
||||
return PTR_ERR(nvddr);
|
||||
} else {
|
||||
sdr = nand_get_sdr_timings(conf);
|
||||
if (IS_ERR(sdr))
|
||||
return PTR_ERR(sdr);
|
||||
}
|
||||
|
||||
if (target < 0)
|
||||
return 0;
|
||||
|
||||
anand->timings = DIFACE_SDR | DIFACE_SDR_MODE(conf->timings.mode);
|
||||
if (nand_interface_is_sdr(conf)) {
|
||||
anand->data_iface = DIFACE_SDR |
|
||||
DIFACE_SDR_MODE(conf->timings.mode);
|
||||
anand->timings = 0;
|
||||
} else {
|
||||
anand->data_iface = DIFACE_NVDDR |
|
||||
DIFACE_DDR_MODE(conf->timings.mode);
|
||||
|
||||
if (conf->timings.nvddr.tCCS_min <= 100000)
|
||||
tccs_min = TCCS_TIME_100NS;
|
||||
else if (conf->timings.nvddr.tCCS_min <= 200000)
|
||||
tccs_min = TCCS_TIME_200NS;
|
||||
else if (conf->timings.nvddr.tCCS_min <= 300000)
|
||||
tccs_min = TCCS_TIME_300NS;
|
||||
else
|
||||
tccs_min = TCCS_TIME_500NS;
|
||||
|
||||
fast_tcad = 0;
|
||||
if (conf->timings.nvddr.tCAD_min < 45000)
|
||||
fast_tcad = FAST_TCAD;
|
||||
|
||||
switch (conf->timings.mode) {
|
||||
case 5:
|
||||
case 4:
|
||||
dqs_mode = 2;
|
||||
break;
|
||||
case 3:
|
||||
dqs_mode = 3;
|
||||
break;
|
||||
case 2:
|
||||
dqs_mode = 4;
|
||||
break;
|
||||
case 1:
|
||||
dqs_mode = 5;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
dqs_mode = 6;
|
||||
break;
|
||||
}
|
||||
|
||||
anand->timings = tccs_min | fast_tcad |
|
||||
DQS_BUFF_SEL_IN(dqs_mode) |
|
||||
DQS_BUFF_SEL_OUT(dqs_mode);
|
||||
}
|
||||
|
||||
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
|
||||
|
||||
/*
|
||||
* Due to a hardware bug in the ZynqMP SoC, SDR timing modes 0-1 work
|
||||
* with f > 90MHz (default clock is 100MHz) but signals are unstable
|
||||
* with higher modes. Hence we decrease a little bit the clock rate to
|
||||
* 80MHz when using modes 2-5 with this SoC.
|
||||
* 80MHz when using SDR modes 2-5 with this SoC.
|
||||
*/
|
||||
if (of_device_is_compatible(np, "xlnx,zynqmp-nand-controller") &&
|
||||
conf->timings.mode >= 2)
|
||||
nand_interface_is_sdr(conf) && conf->timings.mode >= 2)
|
||||
anand->clk = ANFC_XLNX_SDR_HS_CORE_CLK;
|
||||
|
||||
return 0;
|
||||
@ -1007,8 +1169,8 @@ static int anfc_init_hw_ecc_controller(struct arasan_nfc *nfc,
|
||||
if (!anand->bch)
|
||||
return -EINVAL;
|
||||
|
||||
ecc->read_page = anfc_read_page_hw_ecc;
|
||||
ecc->write_page = anfc_write_page_hw_ecc;
|
||||
ecc->read_page = anfc_sel_read_page_hw_ecc;
|
||||
ecc->write_page = anfc_sel_write_page_hw_ecc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1094,37 +1256,43 @@ static int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np)
|
||||
struct anand *anand;
|
||||
struct nand_chip *chip;
|
||||
struct mtd_info *mtd;
|
||||
int cs, rb, ret;
|
||||
int rb, ret, i;
|
||||
|
||||
anand = devm_kzalloc(nfc->dev, sizeof(*anand), GFP_KERNEL);
|
||||
if (!anand)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We do not support multiple CS per chip yet */
|
||||
if (of_property_count_elems_of_size(np, "reg", sizeof(u32)) != 1) {
|
||||
/* Chip-select init */
|
||||
anand->ncs_idx = of_property_count_elems_of_size(np, "reg", sizeof(u32));
|
||||
if (anand->ncs_idx <= 0 || anand->ncs_idx > nfc->ncs) {
|
||||
dev_err(nfc->dev, "Invalid reg property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "reg", &cs);
|
||||
if (ret)
|
||||
return ret;
|
||||
anand->cs_idx = devm_kcalloc(nfc->dev, anand->ncs_idx,
|
||||
sizeof(*anand->cs_idx), GFP_KERNEL);
|
||||
if (!anand->cs_idx)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < anand->ncs_idx; i++) {
|
||||
ret = of_property_read_u32_index(np, "reg", i,
|
||||
&anand->cs_idx[i]);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "invalid CS property: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ready-busy init */
|
||||
ret = of_property_read_u32(np, "nand-rb", &rb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (cs >= ANFC_MAX_CS || rb >= ANFC_MAX_CS) {
|
||||
dev_err(nfc->dev, "Wrong CS %d or RB %d\n", cs, rb);
|
||||
if (rb >= ANFC_MAX_CS) {
|
||||
dev_err(nfc->dev, "Wrong RB %d\n", rb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(cs, &nfc->assigned_cs)) {
|
||||
dev_err(nfc->dev, "Already assigned CS %d\n", cs);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
anand->cs = cs;
|
||||
anand->rb = rb;
|
||||
|
||||
chip = &anand->chip;
|
||||
@ -1140,7 +1308,7 @@ static int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = nand_scan(chip, 1);
|
||||
ret = nand_scan(chip, anand->ncs_idx);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "Scan operation failed\n");
|
||||
return ret;
|
||||
@ -1178,7 +1346,7 @@ static int anfc_chips_init(struct arasan_nfc *nfc)
|
||||
int nchips = of_get_child_count(np);
|
||||
int ret;
|
||||
|
||||
if (!nchips || nchips > ANFC_MAX_CS) {
|
||||
if (!nchips) {
|
||||
dev_err(nfc->dev, "Incorrect number of NAND chips (%d)\n",
|
||||
nchips);
|
||||
return -EINVAL;
|
||||
@ -1203,6 +1371,47 @@ static void anfc_reset(struct arasan_nfc *nfc)
|
||||
|
||||
/* Enable interrupt status */
|
||||
writel_relaxed(EVENT_MASK, nfc->base + INTR_STS_EN_REG);
|
||||
|
||||
nfc->cur_cs = -1;
|
||||
}
|
||||
|
||||
static int anfc_parse_cs(struct arasan_nfc *nfc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check the gpio-cs property */
|
||||
ret = rawnand_dt_parse_gpio_cs(nfc->dev, &nfc->cs_array, &nfc->ncs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The controller native CS cannot be both disabled at the same time.
|
||||
* Hence, only one native CS can be used if GPIO CS are needed, so that
|
||||
* the other is selected when a non-native CS must be asserted (not
|
||||
* wired physically or configured as GPIO instead of NAND CS). In this
|
||||
* case, the "not" chosen CS is assigned to nfc->spare_cs and selected
|
||||
* whenever a GPIO CS must be asserted.
|
||||
*/
|
||||
if (nfc->cs_array && nfc->ncs > 2) {
|
||||
if (!nfc->cs_array[0] && !nfc->cs_array[1]) {
|
||||
dev_err(nfc->dev,
|
||||
"Assign a single native CS when using GPIOs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (nfc->cs_array[0])
|
||||
nfc->spare_cs = 0;
|
||||
else
|
||||
nfc->spare_cs = 1;
|
||||
}
|
||||
|
||||
if (!nfc->cs_array) {
|
||||
nfc->cs_array = anfc_default_cs_array;
|
||||
nfc->ncs = ANFC_MAX_CS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int anfc_probe(struct platform_device *pdev)
|
||||
@ -1241,6 +1450,14 @@ static int anfc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto disable_controller_clk;
|
||||
|
||||
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
|
||||
if (ret)
|
||||
goto disable_bus_clk;
|
||||
|
||||
ret = anfc_parse_cs(nfc);
|
||||
if (ret)
|
||||
goto disable_bus_clk;
|
||||
|
||||
ret = anfc_chips_init(nfc);
|
||||
if (ret)
|
||||
goto disable_bus_clk;
|
||||
|
@ -1246,7 +1246,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
|
||||
nc = to_nand_controller(nand->base.controller);
|
||||
|
||||
/* DDR interface not supported. */
|
||||
if (conf->type != NAND_SDR_IFACE)
|
||||
if (!nand_interface_is_sdr(conf))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/*
|
||||
@ -1524,8 +1524,13 @@ static int atmel_nand_setup_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct atmel_nand *nand = to_atmel_nand(chip);
|
||||
const struct nand_sdr_timings *sdr;
|
||||
struct atmel_nand_controller *nc;
|
||||
|
||||
sdr = nand_get_sdr_timings(conf);
|
||||
if (IS_ERR(sdr))
|
||||
return PTR_ERR(sdr);
|
||||
|
||||
nc = to_nand_controller(nand->base.controller);
|
||||
|
||||
if (csline >= nand->numcs ||
|
||||
|
@ -2348,9 +2348,9 @@ cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
* for tRP and tRH timings. If it is NOT possible to sample data
|
||||
* with optimal tRP/tRH settings, the parameters will be extended.
|
||||
* If clk_period is 50ns (the lowest value) this condition is met
|
||||
* for asynchronous timing modes 1, 2, 3, 4 and 5.
|
||||
* If clk_period is 20ns the condition is met only
|
||||
* for asynchronous timing mode 5.
|
||||
* for SDR timing modes 1, 2, 3, 4 and 5.
|
||||
* If clk_period is 20ns the condition is met only for SDR timing
|
||||
* mode 5.
|
||||
*/
|
||||
if (sdr->tRC_min <= clk_period &&
|
||||
sdr->tRP_min <= (clk_period / 2) &&
|
||||
|
@ -79,7 +79,7 @@ enum gpmi_type {
|
||||
struct gpmi_devdata {
|
||||
enum gpmi_type type;
|
||||
int bch_max_ecc_strength;
|
||||
int max_chain_delay; /* See the async EDO mode */
|
||||
int max_chain_delay; /* See the SDR EDO mode */
|
||||
const char * const *clks;
|
||||
const int clks_count;
|
||||
};
|
||||
|
@ -761,10 +761,8 @@ static int hisi_nfc_probe(struct platform_device *pdev)
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
host->mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(host->mmio)) {
|
||||
dev_err(dev, "devm_ioremap_resource[1] fail\n");
|
||||
if (IS_ERR(host->mmio))
|
||||
return PTR_ERR(host->mmio);
|
||||
}
|
||||
|
||||
mtd->name = "hisi_nand";
|
||||
mtd->dev.parent = &pdev->dev;
|
||||
|
@ -90,9 +90,14 @@ void onfi_fill_interface_config(struct nand_chip *chip,
|
||||
unsigned int timing_mode);
|
||||
unsigned int
|
||||
onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings);
|
||||
unsigned int
|
||||
onfi_find_closest_nvddr_mode(const struct nand_nvddr_timings *spec_timings);
|
||||
int nand_choose_best_sdr_timings(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
struct nand_sdr_timings *spec_timings);
|
||||
int nand_choose_best_nvddr_timings(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
struct nand_nvddr_timings *spec_timings);
|
||||
const struct nand_interface_config *nand_get_reset_interface_config(void);
|
||||
int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
|
||||
int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
|
||||
|
@ -451,7 +451,7 @@ struct marvell_nfc_timings {
|
||||
};
|
||||
|
||||
/**
|
||||
* Derives a duration in numbers of clock cycles.
|
||||
* TO_CYCLES() - Derives a duration in numbers of clock cycles.
|
||||
*
|
||||
* @ps: Duration in pico-seconds
|
||||
* @period_ns: Clock period in nano-seconds
|
||||
@ -3030,8 +3030,10 @@ static int __maybe_unused marvell_nfc_resume(struct device *dev)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(nfc->reg_clk);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
clk_disable_unprepare(nfc->core_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset nfc->selected_chip so the next command will cause the timing
|
||||
|
@ -515,10 +515,8 @@ static int mtk_ecc_probe(struct platform_device *pdev)
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ecc->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(ecc->regs)) {
|
||||
dev_err(dev, "failed to map regs: %ld\n", PTR_ERR(ecc->regs));
|
||||
if (IS_ERR(ecc->regs))
|
||||
return PTR_ERR(ecc->regs);
|
||||
}
|
||||
|
||||
ecc->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(ecc->clk)) {
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include "internals.h"
|
||||
@ -647,7 +648,7 @@ static int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int allowbbt)
|
||||
*/
|
||||
int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
|
||||
{
|
||||
const struct nand_sdr_timings *timings;
|
||||
const struct nand_interface_config *conf;
|
||||
u8 status = 0;
|
||||
int ret;
|
||||
|
||||
@ -655,8 +656,8 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* Wait tWB before polling the STATUS reg. */
|
||||
timings = nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
ndelay(PSEC_TO_NSEC(timings->tWB_max));
|
||||
conf = nand_get_interface_config(chip);
|
||||
ndelay(NAND_COMMON_TIMING_NS(conf, tWB_max));
|
||||
|
||||
ret = nand_status_op(chip, NULL);
|
||||
if (ret)
|
||||
@ -832,7 +833,7 @@ static int nand_reset_interface(struct nand_chip *chip, int chipnr)
|
||||
static int nand_setup_interface(struct nand_chip *chip, int chipnr)
|
||||
{
|
||||
const struct nand_controller_ops *ops = chip->controller->ops;
|
||||
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { };
|
||||
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { }, request;
|
||||
int ret;
|
||||
|
||||
if (!nand_controller_can_setup_interface(chip))
|
||||
@ -848,7 +849,12 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr)
|
||||
if (!chip->best_interface_config)
|
||||
return 0;
|
||||
|
||||
tmode_param[0] = chip->best_interface_config->timings.mode;
|
||||
request = chip->best_interface_config->timings.mode;
|
||||
if (nand_interface_is_sdr(chip->best_interface_config))
|
||||
request |= ONFI_DATA_INTERFACE_SDR;
|
||||
else
|
||||
request |= ONFI_DATA_INTERFACE_NVDDR;
|
||||
tmode_param[0] = request;
|
||||
|
||||
/* Change the mode on the chip side (if supported by the NAND chip) */
|
||||
if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
|
||||
@ -877,9 +883,13 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr)
|
||||
if (ret)
|
||||
goto err_reset_chip;
|
||||
|
||||
if (tmode_param[0] != chip->best_interface_config->timings.mode) {
|
||||
pr_warn("timing mode %d not acknowledged by the NAND chip\n",
|
||||
if (request != tmode_param[0]) {
|
||||
pr_warn("%s timing mode %d not acknowledged by the NAND chip\n",
|
||||
nand_interface_is_nvddr(chip->best_interface_config) ? "NV-DDR" : "SDR",
|
||||
chip->best_interface_config->timings.mode);
|
||||
pr_debug("NAND chip would work in %s timing mode %d\n",
|
||||
tmode_param[0] & ONFI_DATA_INTERFACE_NVDDR ? "NV-DDR" : "SDR",
|
||||
(unsigned int)ONFI_TIMING_MODE_PARAM(tmode_param[0]));
|
||||
goto err_reset_chip;
|
||||
}
|
||||
|
||||
@ -935,7 +945,7 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
|
||||
/* Fallback to slower modes */
|
||||
best_mode = iface->timings.mode;
|
||||
} else if (chip->parameters.onfi) {
|
||||
best_mode = fls(chip->parameters.onfi->async_timing_mode) - 1;
|
||||
best_mode = fls(chip->parameters.onfi->sdr_timing_modes) - 1;
|
||||
}
|
||||
|
||||
for (mode = best_mode; mode >= 0; mode--) {
|
||||
@ -943,13 +953,87 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
|
||||
|
||||
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||
iface);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
chip->best_interface_config = iface;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
chip->best_interface_config = iface;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/**
|
||||
* nand_choose_best_nvddr_timings - Pick up the best NVDDR timings that both the
|
||||
* NAND controller and the NAND chip support
|
||||
* @chip: the NAND chip
|
||||
* @iface: the interface configuration (can eventually be updated)
|
||||
* @spec_timings: specific timings, when not fitting the ONFI specification
|
||||
*
|
||||
* If specific timings are provided, use them. Otherwise, retrieve supported
|
||||
* timing modes from ONFI information.
|
||||
*/
|
||||
int nand_choose_best_nvddr_timings(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
struct nand_nvddr_timings *spec_timings)
|
||||
{
|
||||
const struct nand_controller_ops *ops = chip->controller->ops;
|
||||
int best_mode = 0, mode, ret;
|
||||
|
||||
iface->type = NAND_NVDDR_IFACE;
|
||||
|
||||
if (spec_timings) {
|
||||
iface->timings.nvddr = *spec_timings;
|
||||
iface->timings.mode = onfi_find_closest_nvddr_mode(spec_timings);
|
||||
|
||||
/* Verify the controller supports the requested interface */
|
||||
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||
iface);
|
||||
if (!ret) {
|
||||
chip->best_interface_config = iface;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Fallback to slower modes */
|
||||
best_mode = iface->timings.mode;
|
||||
} else if (chip->parameters.onfi) {
|
||||
best_mode = fls(chip->parameters.onfi->nvddr_timing_modes) - 1;
|
||||
}
|
||||
|
||||
for (mode = best_mode; mode >= 0; mode--) {
|
||||
onfi_fill_interface_config(chip, iface, NAND_NVDDR_IFACE, mode);
|
||||
|
||||
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||
iface);
|
||||
if (!ret) {
|
||||
chip->best_interface_config = iface;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_choose_best_timings - Pick up the best NVDDR or SDR timings that both
|
||||
* NAND controller and the NAND chip support
|
||||
* @chip: the NAND chip
|
||||
* @iface: the interface configuration (can eventually be updated)
|
||||
*
|
||||
* If specific timings are provided, use them. Otherwise, retrieve supported
|
||||
* timing modes from ONFI information.
|
||||
*/
|
||||
static int nand_choose_best_timings(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Try the fastest timings: NV-DDR */
|
||||
ret = nand_choose_best_nvddr_timings(chip, iface, NULL);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
/* Fallback to SDR timings otherwise */
|
||||
return nand_choose_best_sdr_timings(chip, iface, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -980,7 +1064,7 @@ static int nand_choose_interface_config(struct nand_chip *chip)
|
||||
if (chip->ops.choose_interface_config)
|
||||
ret = chip->ops.choose_interface_config(chip, iface);
|
||||
else
|
||||
ret = nand_choose_best_sdr_timings(chip, iface, NULL);
|
||||
ret = nand_choose_best_timings(chip, iface);
|
||||
|
||||
if (ret)
|
||||
kfree(iface);
|
||||
@ -1046,15 +1130,15 @@ static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
|
||||
unsigned int offset_in_page, void *buf,
|
||||
unsigned int len)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u8 addrs[4];
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_READ0, 0),
|
||||
NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
|
||||
PSEC_TO_NSEC(sdr->tRR_min)),
|
||||
NAND_OP_ADDR(3, addrs, NAND_COMMON_TIMING_NS(conf, tWB_max)),
|
||||
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
|
||||
NAND_COMMON_TIMING_NS(conf, tRR_min)),
|
||||
NAND_OP_DATA_IN(len, buf, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
@ -1089,15 +1173,15 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
|
||||
unsigned int offset_in_page, void *buf,
|
||||
unsigned int len)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
u8 addrs[5];
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_READ0, 0),
|
||||
NAND_OP_ADDR(4, addrs, 0),
|
||||
NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
|
||||
PSEC_TO_NSEC(sdr->tRR_min)),
|
||||
NAND_OP_CMD(NAND_CMD_READSTART, NAND_COMMON_TIMING_NS(conf, tWB_max)),
|
||||
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
|
||||
NAND_COMMON_TIMING_NS(conf, tRR_min)),
|
||||
NAND_OP_DATA_IN(len, buf, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
@ -1186,13 +1270,14 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
|
||||
return -EINVAL;
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_PARAM, 0),
|
||||
NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max),
|
||||
PSEC_TO_NSEC(sdr->tRR_min)),
|
||||
NAND_OP_ADDR(1, &page,
|
||||
NAND_COMMON_TIMING_NS(conf, tWB_max)),
|
||||
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
|
||||
NAND_COMMON_TIMING_NS(conf, tRR_min)),
|
||||
NAND_OP_8BIT_DATA_IN(len, buf, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
@ -1241,14 +1326,14 @@ int nand_change_read_column_op(struct nand_chip *chip,
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
u8 addrs[2] = {};
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_RNDOUT, 0),
|
||||
NAND_OP_ADDR(2, addrs, 0),
|
||||
NAND_OP_CMD(NAND_CMD_RNDOUTSTART,
|
||||
PSEC_TO_NSEC(sdr->tCCS_min)),
|
||||
NAND_COMMON_TIMING_NS(conf, tCCS_min)),
|
||||
NAND_OP_DATA_IN(len, buf, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
@ -1316,8 +1401,8 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
|
||||
unsigned int offset_in_page, const void *buf,
|
||||
unsigned int len, bool prog)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u8 addrs[5] = {};
|
||||
struct nand_op_instr instrs[] = {
|
||||
@ -1328,10 +1413,11 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
|
||||
*/
|
||||
NAND_OP_CMD(NAND_CMD_READ0, 0),
|
||||
NAND_OP_CMD(NAND_CMD_SEQIN, 0),
|
||||
NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)),
|
||||
NAND_OP_ADDR(0, addrs, NAND_COMMON_TIMING_NS(conf, tADL_min)),
|
||||
NAND_OP_DATA_OUT(len, buf, 0),
|
||||
NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
|
||||
NAND_OP_CMD(NAND_CMD_PAGEPROG,
|
||||
NAND_COMMON_TIMING_NS(conf, tWB_max)),
|
||||
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max), 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
|
||||
@ -1430,12 +1516,13 @@ int nand_prog_page_end_op(struct nand_chip *chip)
|
||||
u8 status;
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_PAGEPROG,
|
||||
PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
|
||||
NAND_COMMON_TIMING_NS(conf, tWB_max)),
|
||||
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max),
|
||||
0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
|
||||
@ -1548,12 +1635,12 @@ int nand_change_write_column_op(struct nand_chip *chip,
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
u8 addrs[2];
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_RNDIN, 0),
|
||||
NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)),
|
||||
NAND_OP_ADDR(2, addrs, NAND_COMMON_TIMING_NS(conf, tCCS_min)),
|
||||
NAND_OP_DATA_OUT(len, buf, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
@ -1597,26 +1684,46 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
u8 *id = buf;
|
||||
u8 *id = buf, *ddrbuf = NULL;
|
||||
|
||||
if (len && !buf)
|
||||
return -EINVAL;
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_READID, 0),
|
||||
NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)),
|
||||
NAND_OP_ADDR(1, &addr,
|
||||
NAND_COMMON_TIMING_NS(conf, tADL_min)),
|
||||
NAND_OP_8BIT_DATA_IN(len, buf, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
int ret;
|
||||
|
||||
/* READ_ID data bytes are received twice in NV-DDR mode */
|
||||
if (len && nand_interface_is_nvddr(conf)) {
|
||||
ddrbuf = kzalloc(len * 2, GFP_KERNEL);
|
||||
if (!ddrbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
instrs[2].ctx.data.len *= 2;
|
||||
instrs[2].ctx.data.buf.in = ddrbuf;
|
||||
}
|
||||
|
||||
/* Drop the DATA_IN instruction if len is set to 0. */
|
||||
if (!len)
|
||||
op.ninstrs--;
|
||||
|
||||
return nand_exec_op(chip, &op);
|
||||
ret = nand_exec_op(chip, &op);
|
||||
if (!ret && len && nand_interface_is_nvddr(conf)) {
|
||||
for (i = 0; i < len; i++)
|
||||
id[i] = ddrbuf[i * 2];
|
||||
}
|
||||
|
||||
kfree(ddrbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->legacy.cmdfunc(chip, NAND_CMD_READID, addr, -1);
|
||||
@ -1642,19 +1749,31 @@ EXPORT_SYMBOL_GPL(nand_readid_op);
|
||||
int nand_status_op(struct nand_chip *chip, u8 *status)
|
||||
{
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
u8 ddrstatus[2];
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_STATUS,
|
||||
PSEC_TO_NSEC(sdr->tADL_min)),
|
||||
NAND_COMMON_TIMING_NS(conf, tADL_min)),
|
||||
NAND_OP_8BIT_DATA_IN(1, status, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
int ret;
|
||||
|
||||
/* The status data byte will be received twice in NV-DDR mode */
|
||||
if (status && nand_interface_is_nvddr(conf)) {
|
||||
instrs[1].ctx.data.len *= 2;
|
||||
instrs[1].ctx.data.buf.in = ddrstatus;
|
||||
}
|
||||
|
||||
if (!status)
|
||||
op.ninstrs--;
|
||||
|
||||
return nand_exec_op(chip, &op);
|
||||
ret = nand_exec_op(chip, &op);
|
||||
if (!ret && status && nand_interface_is_nvddr(conf))
|
||||
*status = ddrstatus[0];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->legacy.cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
|
||||
@ -1711,15 +1830,16 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
|
||||
u8 status;
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
u8 addrs[3] = { page, page >> 8, page >> 16 };
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_ERASE1, 0),
|
||||
NAND_OP_ADDR(2, addrs, 0),
|
||||
NAND_OP_CMD(NAND_CMD_ERASE2,
|
||||
PSEC_TO_MSEC(sdr->tWB_max)),
|
||||
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0),
|
||||
NAND_COMMON_TIMING_MS(conf, tWB_max)),
|
||||
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tBERS_max),
|
||||
0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
|
||||
@ -1770,14 +1890,17 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
|
||||
int i, ret;
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0),
|
||||
NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)),
|
||||
NAND_OP_ADDR(1, &feature, NAND_COMMON_TIMING_NS(conf,
|
||||
tADL_min)),
|
||||
NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data,
|
||||
PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0),
|
||||
NAND_COMMON_TIMING_NS(conf,
|
||||
tWB_max)),
|
||||
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tFEAT_max),
|
||||
0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
|
||||
@ -1813,23 +1936,37 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
|
||||
static int nand_get_features_op(struct nand_chip *chip, u8 feature,
|
||||
void *data)
|
||||
{
|
||||
u8 *params = data;
|
||||
u8 *params = data, ddrbuf[ONFI_SUBFEATURE_PARAM_LEN * 2];
|
||||
int i;
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0),
|
||||
NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max),
|
||||
PSEC_TO_NSEC(sdr->tRR_min)),
|
||||
NAND_OP_ADDR(1, &feature,
|
||||
NAND_COMMON_TIMING_NS(conf, tWB_max)),
|
||||
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tFEAT_max),
|
||||
NAND_COMMON_TIMING_NS(conf, tRR_min)),
|
||||
NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN,
|
||||
data, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
int ret;
|
||||
|
||||
return nand_exec_op(chip, &op);
|
||||
/* GET_FEATURE data bytes are received twice in NV-DDR mode */
|
||||
if (nand_interface_is_nvddr(conf)) {
|
||||
instrs[3].ctx.data.len *= 2;
|
||||
instrs[3].ctx.data.buf.in = ddrbuf;
|
||||
}
|
||||
|
||||
ret = nand_exec_op(chip, &op);
|
||||
if (nand_interface_is_nvddr(conf)) {
|
||||
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; i++)
|
||||
params[i] = ddrbuf[i * 2];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->legacy.cmdfunc(chip, NAND_CMD_GET_FEATURES, feature, -1);
|
||||
@ -1874,11 +2011,13 @@ static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms,
|
||||
int nand_reset_op(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0),
|
||||
NAND_OP_CMD(NAND_CMD_RESET,
|
||||
NAND_COMMON_TIMING_NS(conf, tWB_max)),
|
||||
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tRST_max),
|
||||
0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
|
||||
@ -1913,17 +2052,50 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,
|
||||
return -EINVAL;
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_interface_config *conf =
|
||||
nand_get_interface_config(chip);
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_DATA_IN(len, buf, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
|
||||
u8 *ddrbuf = NULL;
|
||||
int ret, i;
|
||||
|
||||
instrs[0].ctx.data.force_8bit = force_8bit;
|
||||
|
||||
if (check_only)
|
||||
return nand_check_op(chip, &op);
|
||||
/*
|
||||
* Parameter payloads (ID, status, features, etc) do not go
|
||||
* through the same pipeline as regular data, hence the
|
||||
* force_8bit flag must be set and this also indicates that in
|
||||
* case NV-DDR timings are being used the data will be received
|
||||
* twice.
|
||||
*/
|
||||
if (force_8bit && nand_interface_is_nvddr(conf)) {
|
||||
ddrbuf = kzalloc(len * 2, GFP_KERNEL);
|
||||
if (!ddrbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
return nand_exec_op(chip, &op);
|
||||
instrs[0].ctx.data.len *= 2;
|
||||
instrs[0].ctx.data.buf.in = ddrbuf;
|
||||
}
|
||||
|
||||
if (check_only) {
|
||||
ret = nand_check_op(chip, &op);
|
||||
kfree(ddrbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nand_exec_op(chip, &op);
|
||||
if (!ret && force_8bit && nand_interface_is_nvddr(conf)) {
|
||||
u8 *dst = buf;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = ddrbuf[i * 2];
|
||||
}
|
||||
|
||||
kfree(ddrbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (check_only)
|
||||
@ -3136,13 +3308,13 @@ static int nand_setup_read_retry(struct nand_chip *chip, int retry_mode)
|
||||
|
||||
static void nand_wait_readrdy(struct nand_chip *chip)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr;
|
||||
const struct nand_interface_config *conf;
|
||||
|
||||
if (!(chip->options & NAND_NEED_READRDY))
|
||||
return;
|
||||
|
||||
sdr = nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
WARN_ON(nand_wait_rdy_op(chip, PSEC_TO_MSEC(sdr->tR_max), 0));
|
||||
conf = nand_get_interface_config(chip);
|
||||
WARN_ON(nand_wait_rdy_op(chip, NAND_COMMON_TIMING_MS(conf, tR_max), 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5078,6 +5250,44 @@ static int of_get_nand_secure_regions(struct nand_chip *chip)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rawnand_dt_parse_gpio_cs - Parse the gpio-cs property of a controller
|
||||
* @dev: Device that will be parsed. Also used for managed allocations.
|
||||
* @cs_array: Array of GPIO desc pointers allocated on success
|
||||
* @ncs_array: Number of entries in @cs_array updated on success.
|
||||
* @return 0 on success, an error otherwise.
|
||||
*/
|
||||
int rawnand_dt_parse_gpio_cs(struct device *dev, struct gpio_desc ***cs_array,
|
||||
unsigned int *ncs_array)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct gpio_desc **descs;
|
||||
int ndescs, i;
|
||||
|
||||
ndescs = of_gpio_named_count(np, "cs-gpios");
|
||||
if (ndescs < 0) {
|
||||
dev_dbg(dev, "No valid cs-gpios property\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
descs = devm_kcalloc(dev, ndescs, sizeof(*descs), GFP_KERNEL);
|
||||
if (!descs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ndescs; i++) {
|
||||
descs[i] = gpiod_get_index_optional(dev, "cs", i,
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(descs[i]))
|
||||
return PTR_ERR(descs[i]);
|
||||
}
|
||||
|
||||
*ncs_array = ndescs;
|
||||
*cs_array = descs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rawnand_dt_parse_gpio_cs);
|
||||
|
||||
static int rawnand_dt_init(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(chip));
|
||||
|
@ -369,7 +369,7 @@ static void nand_ccs_delay(struct nand_chip *chip)
|
||||
* Wait tCCS_min if it is correctly defined, otherwise wait 500ns
|
||||
* (which should be safe for all NANDs).
|
||||
*/
|
||||
if (nand_controller_can_setup_interface(chip))
|
||||
if (!IS_ERR(sdr) && nand_controller_can_setup_interface(chip))
|
||||
ndelay(sdr->tCCS_min / 1000);
|
||||
else
|
||||
ndelay(500);
|
||||
|
@ -315,7 +315,10 @@ int nand_onfi_detect(struct nand_chip *chip)
|
||||
onfi->tBERS = le16_to_cpu(p->t_bers);
|
||||
onfi->tR = le16_to_cpu(p->t_r);
|
||||
onfi->tCCS = le16_to_cpu(p->t_ccs);
|
||||
onfi->async_timing_mode = le16_to_cpu(p->async_timing_mode);
|
||||
onfi->fast_tCAD = le16_to_cpu(p->nvddr_nvddr2_features) & BIT(0);
|
||||
onfi->sdr_timing_modes = le16_to_cpu(p->sdr_timing_modes);
|
||||
if (le16_to_cpu(p->features) & ONFI_FEATURE_NV_DDR)
|
||||
onfi->nvddr_timing_modes = le16_to_cpu(p->nvddr_timing_modes);
|
||||
onfi->vendor_revision = le16_to_cpu(p->vendor_revision);
|
||||
memcpy(onfi->vendor, p->vendor, sizeof(p->vendor));
|
||||
chip->parameters.onfi = onfi;
|
||||
|
@ -292,6 +292,261 @@ static const struct nand_interface_config onfi_sdr_timings[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct nand_interface_config onfi_nvddr_timings[] = {
|
||||
/* Mode 0 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 0,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 10000,
|
||||
.tCALH_min = 10000,
|
||||
.tCALS_min = 10000,
|
||||
.tCAS_min = 10000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 10000,
|
||||
.tCK_min = 50000,
|
||||
.tCS_min = 35000,
|
||||
.tDH_min = 5000,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 5000,
|
||||
.tDS_min = 5000,
|
||||
.tDSC_min = 50000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 6000,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 1 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 1,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 5000,
|
||||
.tCALH_min = 5000,
|
||||
.tCALS_min = 5000,
|
||||
.tCAS_min = 5000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCK_min = 30000,
|
||||
.tCS_min = 25000,
|
||||
.tDH_min = 2500,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 2500,
|
||||
.tDS_min = 3000,
|
||||
.tDSC_min = 30000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 3000,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 2 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 2,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 4000,
|
||||
.tCALH_min = 4000,
|
||||
.tCALS_min = 4000,
|
||||
.tCAS_min = 4000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 4000,
|
||||
.tCK_min = 20000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 1700,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 1700,
|
||||
.tDS_min = 2000,
|
||||
.tDSC_min = 20000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 2000,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 3 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 3,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 3000,
|
||||
.tCALH_min = 3000,
|
||||
.tCALS_min = 3000,
|
||||
.tCAS_min = 3000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 3000,
|
||||
.tCK_min = 15000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 1300,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 1300,
|
||||
.tDS_min = 1500,
|
||||
.tDSC_min = 15000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 1500,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 4 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 4,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 2500,
|
||||
.tCALH_min = 2500,
|
||||
.tCALS_min = 2500,
|
||||
.tCAS_min = 2500,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 2500,
|
||||
.tCK_min = 12000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 1100,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 1000,
|
||||
.tDS_min = 1100,
|
||||
.tDSC_min = 12000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 1200,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 5 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 5,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 2000,
|
||||
.tCALH_min = 2000,
|
||||
.tCALS_min = 2000,
|
||||
.tCAS_min = 2000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 2000,
|
||||
.tCK_min = 10000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 900,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 850,
|
||||
.tDS_min = 900,
|
||||
.tDSC_min = 10000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 1000,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* All NAND chips share the same reset data interface: SDR mode 0 */
|
||||
const struct nand_interface_config *nand_get_reset_interface_config(void)
|
||||
{
|
||||
@ -346,23 +601,60 @@ onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings)
|
||||
}
|
||||
|
||||
/**
|
||||
* onfi_fill_interface_config - Initialize an interface config from a given
|
||||
* ONFI mode
|
||||
* onfi_find_closest_nvddr_mode - Derive the closest ONFI NVDDR timing mode
|
||||
* given a set of timings
|
||||
* @spec_timings: the timings to challenge
|
||||
*/
|
||||
unsigned int
|
||||
onfi_find_closest_nvddr_mode(const struct nand_nvddr_timings *spec_timings)
|
||||
{
|
||||
const struct nand_nvddr_timings *onfi_timings;
|
||||
int mode;
|
||||
|
||||
for (mode = ARRAY_SIZE(onfi_nvddr_timings) - 1; mode > 0; mode--) {
|
||||
onfi_timings = &onfi_nvddr_timings[mode].timings.nvddr;
|
||||
|
||||
if (spec_timings->tCCS_min <= onfi_timings->tCCS_min &&
|
||||
spec_timings->tAC_min <= onfi_timings->tAC_min &&
|
||||
spec_timings->tADL_min <= onfi_timings->tADL_min &&
|
||||
spec_timings->tCAD_min <= onfi_timings->tCAD_min &&
|
||||
spec_timings->tCAH_min <= onfi_timings->tCAH_min &&
|
||||
spec_timings->tCALH_min <= onfi_timings->tCALH_min &&
|
||||
spec_timings->tCALS_min <= onfi_timings->tCALS_min &&
|
||||
spec_timings->tCAS_min <= onfi_timings->tCAS_min &&
|
||||
spec_timings->tCEH_min <= onfi_timings->tCEH_min &&
|
||||
spec_timings->tCH_min <= onfi_timings->tCH_min &&
|
||||
spec_timings->tCK_min <= onfi_timings->tCK_min &&
|
||||
spec_timings->tCS_min <= onfi_timings->tCS_min &&
|
||||
spec_timings->tDH_min <= onfi_timings->tDH_min &&
|
||||
spec_timings->tDQSCK_min <= onfi_timings->tDQSCK_min &&
|
||||
spec_timings->tDQSD_min <= onfi_timings->tDQSD_min &&
|
||||
spec_timings->tDS_min <= onfi_timings->tDS_min &&
|
||||
spec_timings->tDSC_min <= onfi_timings->tDSC_min &&
|
||||
spec_timings->tRHW_min <= onfi_timings->tRHW_min &&
|
||||
spec_timings->tRR_min <= onfi_timings->tRR_min &&
|
||||
spec_timings->tWHR_min <= onfi_timings->tWHR_min &&
|
||||
spec_timings->tWRCK_min <= onfi_timings->tWRCK_min &&
|
||||
spec_timings->tWW_min <= onfi_timings->tWW_min)
|
||||
return mode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* onfi_fill_sdr_interface_config - Initialize a SDR interface config from a
|
||||
* given ONFI mode
|
||||
* @chip: The NAND chip
|
||||
* @iface: The interface configuration to fill
|
||||
* @type: The interface type
|
||||
* @timing_mode: The ONFI timing mode
|
||||
*/
|
||||
void onfi_fill_interface_config(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
enum nand_interface_type type,
|
||||
unsigned int timing_mode)
|
||||
static void onfi_fill_sdr_interface_config(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
unsigned int timing_mode)
|
||||
{
|
||||
struct onfi_params *onfi = chip->parameters.onfi;
|
||||
|
||||
if (WARN_ON(type != NAND_SDR_IFACE))
|
||||
return;
|
||||
|
||||
if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_sdr_timings)))
|
||||
return;
|
||||
|
||||
@ -385,3 +677,61 @@ void onfi_fill_interface_config(struct nand_chip *chip,
|
||||
timings->tCCS_min = 1000UL * onfi->tCCS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onfi_fill_nvddr_interface_config - Initialize a NVDDR interface config from a
|
||||
* given ONFI mode
|
||||
* @chip: The NAND chip
|
||||
* @iface: The interface configuration to fill
|
||||
* @timing_mode: The ONFI timing mode
|
||||
*/
|
||||
static void onfi_fill_nvddr_interface_config(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
unsigned int timing_mode)
|
||||
{
|
||||
struct onfi_params *onfi = chip->parameters.onfi;
|
||||
|
||||
if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_nvddr_timings)))
|
||||
return;
|
||||
|
||||
*iface = onfi_nvddr_timings[timing_mode];
|
||||
|
||||
/*
|
||||
* Initialize timings that cannot be deduced from timing mode:
|
||||
* tPROG, tBERS, tR, tCCS and tCAD.
|
||||
* These information are part of the ONFI parameter page.
|
||||
*/
|
||||
if (onfi) {
|
||||
struct nand_nvddr_timings *timings = &iface->timings.nvddr;
|
||||
|
||||
/* microseconds -> picoseconds */
|
||||
timings->tPROG_max = 1000000ULL * onfi->tPROG;
|
||||
timings->tBERS_max = 1000000ULL * onfi->tBERS;
|
||||
timings->tR_max = 1000000ULL * onfi->tR;
|
||||
|
||||
/* nanoseconds -> picoseconds */
|
||||
timings->tCCS_min = 1000UL * onfi->tCCS;
|
||||
|
||||
if (onfi->fast_tCAD)
|
||||
timings->tCAD_min = 25000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onfi_fill_interface_config - Initialize an interface config from a given
|
||||
* ONFI mode
|
||||
* @chip: The NAND chip
|
||||
* @iface: The interface configuration to fill
|
||||
* @type: The interface type
|
||||
* @timing_mode: The ONFI timing mode
|
||||
*/
|
||||
void onfi_fill_interface_config(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
enum nand_interface_type type,
|
||||
unsigned int timing_mode)
|
||||
{
|
||||
if (type == NAND_SDR_IFACE)
|
||||
return onfi_fill_sdr_interface_config(chip, iface, timing_mode);
|
||||
else
|
||||
return onfi_fill_nvddr_interface_config(chip, iface, timing_mode);
|
||||
}
|
||||
|
@ -131,7 +131,7 @@
|
||||
#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */
|
||||
#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */
|
||||
|
||||
#define BADBLOCK_MARKER_LENGTH 2
|
||||
#define BBM_LEN 2
|
||||
|
||||
static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
|
||||
0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
|
||||
@ -171,6 +171,10 @@ struct omap_nand_info {
|
||||
struct device *elm_dev;
|
||||
/* NAND ready gpio */
|
||||
struct gpio_desc *ready_gpiod;
|
||||
unsigned int neccpg;
|
||||
unsigned int nsteps_per_eccpg;
|
||||
unsigned int eccpg_size;
|
||||
unsigned int eccpg_bytes;
|
||||
};
|
||||
|
||||
static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
|
||||
@ -1355,7 +1359,7 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data,
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
struct nand_ecc_ctrl *ecc = &info->nand.ecc;
|
||||
int eccsteps = info->nand.ecc.steps;
|
||||
int eccsteps = info->nsteps_per_eccpg;
|
||||
int i , j, stat = 0;
|
||||
int eccflag, actual_eccbytes;
|
||||
struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
|
||||
@ -1525,25 +1529,38 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
uint8_t *ecc_calc = chip->ecc.calc_buf;
|
||||
unsigned int eccpg;
|
||||
int ret;
|
||||
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
|
||||
/* Enable GPMC ecc engine */
|
||||
chip->ecc.hwctl(chip, NAND_ECC_WRITE);
|
||||
|
||||
/* Write data */
|
||||
chip->legacy.write_buf(chip, buf, mtd->writesize);
|
||||
|
||||
/* Update ecc vector from GPMC result registers */
|
||||
omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]);
|
||||
|
||||
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
|
||||
chip->ecc.total);
|
||||
ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
|
||||
/* Enable GPMC ecc engine */
|
||||
chip->ecc.hwctl(chip, NAND_ECC_WRITE);
|
||||
|
||||
/* Write data */
|
||||
chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
|
||||
info->eccpg_size);
|
||||
|
||||
/* Update ecc vector from GPMC result registers */
|
||||
ret = omap_calculate_ecc_bch_multi(mtd,
|
||||
buf + (eccpg * info->eccpg_size),
|
||||
ecc_calc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc,
|
||||
chip->oob_poi,
|
||||
eccpg * info->eccpg_bytes,
|
||||
info->eccpg_bytes);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write ecc vector to OOB area */
|
||||
chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
@ -1566,12 +1583,13 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
|
||||
int oob_required, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
u8 *ecc_calc = chip->ecc.calc_buf;
|
||||
int ecc_size = chip->ecc.size;
|
||||
int ecc_bytes = chip->ecc.bytes;
|
||||
int ecc_steps = chip->ecc.steps;
|
||||
u32 start_step = offset / ecc_size;
|
||||
u32 end_step = (offset + data_len - 1) / ecc_size;
|
||||
unsigned int eccpg;
|
||||
int step, ret = 0;
|
||||
|
||||
/*
|
||||
@ -1580,36 +1598,48 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
|
||||
* ECC is calculated for all subpages but we choose
|
||||
* only what we want.
|
||||
*/
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
|
||||
/* Enable GPMC ECC engine */
|
||||
chip->ecc.hwctl(chip, NAND_ECC_WRITE);
|
||||
|
||||
/* Write data */
|
||||
chip->legacy.write_buf(chip, buf, mtd->writesize);
|
||||
|
||||
for (step = 0; step < ecc_steps; step++) {
|
||||
/* mask ECC of un-touched subpages by padding 0xFF */
|
||||
if (step < start_step || step > end_step)
|
||||
memset(ecc_calc, 0xff, ecc_bytes);
|
||||
else
|
||||
ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf += ecc_size;
|
||||
ecc_calc += ecc_bytes;
|
||||
}
|
||||
|
||||
/* copy calculated ECC for whole page to chip->buffer->oob */
|
||||
/* this include masked-value(0xFF) for unwritten subpages */
|
||||
ecc_calc = chip->ecc.calc_buf;
|
||||
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
|
||||
chip->ecc.total);
|
||||
ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
|
||||
/* Enable GPMC ECC engine */
|
||||
chip->ecc.hwctl(chip, NAND_ECC_WRITE);
|
||||
|
||||
/* Write data */
|
||||
chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
|
||||
info->eccpg_size);
|
||||
|
||||
for (step = 0; step < info->nsteps_per_eccpg; step++) {
|
||||
unsigned int base_step = eccpg * info->nsteps_per_eccpg;
|
||||
const u8 *bufoffs = buf + (eccpg * info->eccpg_size);
|
||||
|
||||
/* Mask ECC of un-touched subpages with 0xFFs */
|
||||
if ((step + base_step) < start_step ||
|
||||
(step + base_step) > end_step)
|
||||
memset(ecc_calc + (step * ecc_bytes), 0xff,
|
||||
ecc_bytes);
|
||||
else
|
||||
ret = _omap_calculate_ecc_bch(mtd,
|
||||
bufoffs + (step * ecc_size),
|
||||
ecc_calc + (step * ecc_bytes),
|
||||
step);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the calculated ECC for the whole page including the
|
||||
* masked values (0xFF) corresponding to unwritten subpages.
|
||||
*/
|
||||
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi,
|
||||
eccpg * info->eccpg_bytes,
|
||||
info->eccpg_bytes);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* write OOB buffer to NAND device */
|
||||
chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
@ -1634,40 +1664,60 @@ static int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
uint8_t *ecc_calc = chip->ecc.calc_buf;
|
||||
uint8_t *ecc_code = chip->ecc.code_buf;
|
||||
unsigned int max_bitflips = 0, eccpg;
|
||||
int stat, ret;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
|
||||
/* Enable GPMC ecc engine */
|
||||
chip->ecc.hwctl(chip, NAND_ECC_READ);
|
||||
|
||||
/* Read data */
|
||||
chip->legacy.read_buf(chip, buf, mtd->writesize);
|
||||
|
||||
/* Read oob bytes */
|
||||
nand_change_read_column_op(chip,
|
||||
mtd->writesize + BADBLOCK_MARKER_LENGTH,
|
||||
chip->oob_poi + BADBLOCK_MARKER_LENGTH,
|
||||
chip->ecc.total, false);
|
||||
|
||||
/* Calculate ecc bytes */
|
||||
omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc);
|
||||
|
||||
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
|
||||
chip->ecc.total);
|
||||
ret = nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stat = chip->ecc.correct(chip, buf, ecc_code, ecc_calc);
|
||||
for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
|
||||
/* Enable GPMC ecc engine */
|
||||
chip->ecc.hwctl(chip, NAND_ECC_READ);
|
||||
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
/* Read data */
|
||||
ret = nand_change_read_column_op(chip, eccpg * info->eccpg_size,
|
||||
buf + (eccpg * info->eccpg_size),
|
||||
info->eccpg_size, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read oob bytes */
|
||||
ret = nand_change_read_column_op(chip,
|
||||
mtd->writesize + BBM_LEN +
|
||||
(eccpg * info->eccpg_bytes),
|
||||
chip->oob_poi + BBM_LEN +
|
||||
(eccpg * info->eccpg_bytes),
|
||||
info->eccpg_bytes, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Calculate ecc bytes */
|
||||
ret = omap_calculate_ecc_bch_multi(mtd,
|
||||
buf + (eccpg * info->eccpg_size),
|
||||
ecc_calc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code,
|
||||
chip->oob_poi,
|
||||
eccpg * info->eccpg_bytes,
|
||||
info->eccpg_bytes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stat = chip->ecc.correct(chip,
|
||||
buf + (eccpg * info->eccpg_size),
|
||||
ecc_code, ecc_calc);
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
|
||||
return max_bitflips;
|
||||
@ -1820,7 +1870,7 @@ static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
struct nand_chip *chip = &info->nand;
|
||||
int off = BADBLOCK_MARKER_LENGTH;
|
||||
int off = BBM_LEN;
|
||||
|
||||
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
|
||||
!(chip->options & NAND_BUSWIDTH_16))
|
||||
@ -1840,7 +1890,7 @@ static int omap_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
struct nand_chip *chip = &info->nand;
|
||||
int off = BADBLOCK_MARKER_LENGTH;
|
||||
int off = BBM_LEN;
|
||||
|
||||
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
|
||||
!(chip->options & NAND_BUSWIDTH_16))
|
||||
@ -1870,7 +1920,7 @@ static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
|
||||
unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
|
||||
int off = BADBLOCK_MARKER_LENGTH;
|
||||
int off = BBM_LEN;
|
||||
|
||||
if (section >= nsteps)
|
||||
return -ERANGE;
|
||||
@ -1891,7 +1941,7 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
|
||||
unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
|
||||
int off = BADBLOCK_MARKER_LENGTH;
|
||||
int off = BBM_LEN;
|
||||
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
@ -1920,7 +1970,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
struct device *dev = &info->pdev->dev;
|
||||
int min_oobbytes = BADBLOCK_MARKER_LENGTH;
|
||||
int min_oobbytes = BBM_LEN;
|
||||
int elm_bch_strength = -1;
|
||||
int oobbytes_per_step;
|
||||
dma_cap_mask_t mask;
|
||||
int err;
|
||||
@ -2074,12 +2125,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
chip->ecc.write_subpage = omap_write_subpage_bch;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, BCH4_ECC,
|
||||
mtd->writesize / chip->ecc.size,
|
||||
chip->ecc.size, chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
return err;
|
||||
elm_bch_strength = BCH4_ECC;
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||
@ -2116,13 +2162,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
chip->ecc.write_subpage = omap_write_subpage_bch;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, BCH8_ECC,
|
||||
mtd->writesize / chip->ecc.size,
|
||||
chip->ecc.size, chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
elm_bch_strength = BCH8_ECC;
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
@ -2138,19 +2178,32 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
chip->ecc.write_subpage = omap_write_subpage_bch;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, BCH16_ECC,
|
||||
mtd->writesize / chip->ecc.size,
|
||||
chip->ecc.size, chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
elm_bch_strength = BCH16_ECC;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid or unsupported ECC scheme\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (elm_bch_strength >= 0) {
|
||||
chip->ecc.steps = mtd->writesize / chip->ecc.size;
|
||||
info->neccpg = chip->ecc.steps / ERROR_VECTOR_MAX;
|
||||
if (info->neccpg) {
|
||||
info->nsteps_per_eccpg = ERROR_VECTOR_MAX;
|
||||
} else {
|
||||
info->neccpg = 1;
|
||||
info->nsteps_per_eccpg = chip->ecc.steps;
|
||||
}
|
||||
info->eccpg_size = info->nsteps_per_eccpg * chip->ecc.size;
|
||||
info->eccpg_bytes = info->nsteps_per_eccpg * chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, elm_bch_strength,
|
||||
info->nsteps_per_eccpg, chip->ecc.size,
|
||||
chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check if NAND device's OOB is enough to store ECC signatures */
|
||||
min_oobbytes += (oobbytes_per_step *
|
||||
(mtd->writesize / chip->ecc.size));
|
||||
|
@ -116,7 +116,7 @@ int elm_config(struct device *dev, enum bch_ecc bch_type,
|
||||
return -EINVAL;
|
||||
}
|
||||
/* ELM support 8 error syndrome process */
|
||||
if (ecc_steps > ERROR_VECTOR_MAX) {
|
||||
if (ecc_steps > ERROR_VECTOR_MAX && ecc_steps % ERROR_VECTOR_MAX) {
|
||||
dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
1194
drivers/mtd/nand/raw/pl35x-nand-controller.c
Normal file
1194
drivers/mtd/nand/raw/pl35x-nand-controller.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -734,6 +734,7 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, i
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
u32 cmd, cfg0, cfg1, ecc_bch_cfg;
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
|
||||
if (read) {
|
||||
if (host->use_ecc)
|
||||
@ -762,7 +763,8 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, i
|
||||
nandc_set_reg(chip, NAND_DEV0_CFG0, cfg0);
|
||||
nandc_set_reg(chip, NAND_DEV0_CFG1, cfg1);
|
||||
nandc_set_reg(chip, NAND_DEV0_ECC_CFG, ecc_bch_cfg);
|
||||
nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg);
|
||||
if (!nandc->props->qpic_v2)
|
||||
nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg);
|
||||
nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus);
|
||||
nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus);
|
||||
nandc_set_reg(chip, NAND_EXEC_CMD, 1);
|
||||
@ -1133,7 +1135,8 @@ static void config_nand_page_read(struct nand_chip *chip)
|
||||
|
||||
write_reg_dma(nandc, NAND_ADDR0, 2, 0);
|
||||
write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
|
||||
write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
|
||||
if (!nandc->props->qpic_v2)
|
||||
write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
|
||||
write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0);
|
||||
write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1,
|
||||
NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
|
||||
@ -1191,8 +1194,9 @@ static void config_nand_page_write(struct nand_chip *chip)
|
||||
|
||||
write_reg_dma(nandc, NAND_ADDR0, 2, 0);
|
||||
write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
|
||||
write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
|
||||
NAND_BAM_NEXT_SGL);
|
||||
if (!nandc->props->qpic_v2)
|
||||
write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
|
||||
NAND_BAM_NEXT_SGL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1248,7 +1252,8 @@ static int nandc_param(struct qcom_nand_host *host)
|
||||
| 2 << WR_RD_BSY_GAP
|
||||
| 0 << WIDE_FLASH
|
||||
| 1 << DEV0_CFG1_ECC_DISABLE);
|
||||
nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
|
||||
if (!nandc->props->qpic_v2)
|
||||
nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
|
||||
|
||||
/* configure CMD1 and VLD for ONFI param probing in QPIC v1 */
|
||||
if (!nandc->props->qpic_v2) {
|
||||
@ -1850,8 +1855,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
||||
* ERASED_CW bits are set.
|
||||
*/
|
||||
if (host->bch_enabled) {
|
||||
erased = (erased_cw & ERASED_CW) == ERASED_CW ?
|
||||
true : false;
|
||||
erased = (erased_cw & ERASED_CW) == ERASED_CW;
|
||||
/*
|
||||
* For RS ECC, HW reports the erased CW by placing
|
||||
* special characters at certain offsets in the buffer.
|
||||
@ -2689,7 +2693,8 @@ static int qcom_nand_attach_chip(struct nand_chip *chip)
|
||||
| ecc_mode << ECC_MODE
|
||||
| host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH;
|
||||
|
||||
host->ecc_buf_cfg = 0x203 << NUM_STEPS;
|
||||
if (!nandc->props->qpic_v2)
|
||||
host->ecc_buf_cfg = 0x203 << NUM_STEPS;
|
||||
|
||||
host->clrflashstatus = FS_READY_BSY_N;
|
||||
host->clrreadstatus = 0xc0;
|
||||
@ -2882,7 +2887,7 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const probes[] = { "qcomsmem", NULL };
|
||||
static const char * const probes[] = { "cmdlinepart", "ofpart", "qcomsmem", NULL };
|
||||
|
||||
static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
|
||||
struct qcom_nand_host *host,
|
||||
|
@ -583,8 +583,8 @@ static void r852_update_card_detect(struct r852_device *dev)
|
||||
r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg);
|
||||
}
|
||||
|
||||
static ssize_t r852_media_type_show(struct device *sys_dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t media_type_show(struct device *sys_dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev);
|
||||
struct r852_device *dev = r852_get_dev(mtd);
|
||||
@ -593,8 +593,7 @@ static ssize_t r852_media_type_show(struct device *sys_dev,
|
||||
strcpy(buf, data);
|
||||
return strlen(data);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL);
|
||||
static DEVICE_ATTR_RO(media_type);
|
||||
|
||||
|
||||
/* Detect properties of card in slot */
|
||||
|
@ -138,20 +138,12 @@ int spinand_select_target(struct spinand_device *spinand, unsigned int target)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinand_init_cfg_cache(struct spinand_device *spinand)
|
||||
static int spinand_read_cfg(struct spinand_device *spinand)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
struct device *dev = &spinand->spimem->spi->dev;
|
||||
unsigned int target;
|
||||
int ret;
|
||||
|
||||
spinand->cfg_cache = devm_kcalloc(dev,
|
||||
nand->memorg.ntargets,
|
||||
sizeof(*spinand->cfg_cache),
|
||||
GFP_KERNEL);
|
||||
if (!spinand->cfg_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
for (target = 0; target < nand->memorg.ntargets; target++) {
|
||||
ret = spinand_select_target(spinand, target);
|
||||
if (ret)
|
||||
@ -170,6 +162,21 @@ static int spinand_init_cfg_cache(struct spinand_device *spinand)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinand_init_cfg_cache(struct spinand_device *spinand)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
struct device *dev = &spinand->spimem->spi->dev;
|
||||
|
||||
spinand->cfg_cache = devm_kcalloc(dev,
|
||||
nand->memorg.ntargets,
|
||||
sizeof(*spinand->cfg_cache),
|
||||
GFP_KERNEL);
|
||||
if (!spinand->cfg_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinand_init_quad_enable(struct spinand_device *spinand)
|
||||
{
|
||||
bool enable = false;
|
||||
@ -290,6 +297,8 @@ static int spinand_ondie_ecc_finish_io_req(struct nand_device *nand,
|
||||
{
|
||||
struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv;
|
||||
struct spinand_device *spinand = nand_to_spinand(nand);
|
||||
struct mtd_info *mtd = spinand_to_mtd(spinand);
|
||||
int ret;
|
||||
|
||||
if (req->mode == MTD_OPS_RAW)
|
||||
return 0;
|
||||
@ -299,7 +308,13 @@ static int spinand_ondie_ecc_finish_io_req(struct nand_device *nand,
|
||||
return 0;
|
||||
|
||||
/* Finish a page write: check the status, report errors/bitflips */
|
||||
return spinand_check_ecc_status(spinand, engine_conf->status);
|
||||
ret = spinand_check_ecc_status(spinand, engine_conf->status);
|
||||
if (ret == -EBADMSG)
|
||||
mtd->ecc_stats.failed++;
|
||||
else if (ret > 0)
|
||||
mtd->ecc_stats.corrected += ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nand_ecc_engine_ops spinand_ondie_ecc_engine_ops = {
|
||||
@ -620,13 +635,10 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
|
||||
if (ret < 0 && ret != -EBADMSG)
|
||||
break;
|
||||
|
||||
if (ret == -EBADMSG) {
|
||||
if (ret == -EBADMSG)
|
||||
ecc_failed = true;
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += ret;
|
||||
else
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, ret);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
ops->retlen += iter.req.datalen;
|
||||
@ -1074,12 +1086,71 @@ static int spinand_detect(struct spinand_device *spinand)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinand_init_flash(struct spinand_device *spinand)
|
||||
{
|
||||
struct device *dev = &spinand->spimem->spi->dev;
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
int ret, i;
|
||||
|
||||
ret = spinand_read_cfg(spinand);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spinand_init_quad_enable(spinand);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spinand_manufacturer_init(spinand);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to initialize the SPI NAND chip (err = %d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* After power up, all blocks are locked, so unlock them here. */
|
||||
for (i = 0; i < nand->memorg.ntargets; i++) {
|
||||
ret = spinand_select_target(spinand, i);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
spinand_manufacturer_cleanup(spinand);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void spinand_mtd_resume(struct mtd_info *mtd)
|
||||
{
|
||||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
int ret;
|
||||
|
||||
ret = spinand_reset_op(spinand);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
ret = spinand_init_flash(spinand);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
spinand_ecc_enable(spinand, false);
|
||||
}
|
||||
|
||||
static int spinand_init(struct spinand_device *spinand)
|
||||
{
|
||||
struct device *dev = &spinand->spimem->spi->dev;
|
||||
struct mtd_info *mtd = spinand_to_mtd(spinand);
|
||||
struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We need a scratch buffer because the spi_mem interface requires that
|
||||
@ -1112,22 +1183,10 @@ static int spinand_init(struct spinand_device *spinand)
|
||||
if (ret)
|
||||
goto err_free_bufs;
|
||||
|
||||
ret = spinand_init_quad_enable(spinand);
|
||||
ret = spinand_init_flash(spinand);
|
||||
if (ret)
|
||||
goto err_free_bufs;
|
||||
|
||||
ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
|
||||
if (ret)
|
||||
goto err_free_bufs;
|
||||
|
||||
ret = spinand_manufacturer_init(spinand);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to initialize the SPI NAND chip (err = %d)\n",
|
||||
ret);
|
||||
goto err_free_bufs;
|
||||
}
|
||||
|
||||
ret = spinand_create_dirmaps(spinand);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
@ -1136,17 +1195,6 @@ static int spinand_init(struct spinand_device *spinand)
|
||||
goto err_manuf_cleanup;
|
||||
}
|
||||
|
||||
/* After power up, all blocks are locked, so unlock them here. */
|
||||
for (i = 0; i < nand->memorg.ntargets; i++) {
|
||||
ret = spinand_select_target(spinand, i);
|
||||
if (ret)
|
||||
goto err_manuf_cleanup;
|
||||
|
||||
ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED);
|
||||
if (ret)
|
||||
goto err_manuf_cleanup;
|
||||
}
|
||||
|
||||
ret = nanddev_init(nand, &spinand_ops, THIS_MODULE);
|
||||
if (ret)
|
||||
goto err_manuf_cleanup;
|
||||
@ -1167,6 +1215,7 @@ static int spinand_init(struct spinand_device *spinand)
|
||||
mtd->_block_isreserved = spinand_mtd_block_isreserved;
|
||||
mtd->_erase = spinand_mtd_erase;
|
||||
mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
|
||||
mtd->_resume = spinand_mtd_resume;
|
||||
|
||||
if (nand->ecc.engine) {
|
||||
ret = mtd_ooblayout_count_freebytes(mtd);
|
||||
|
@ -186,6 +186,118 @@ static const struct spinand_info macronix_spinand_table[] = {
|
||||
0 /*SPINAND_HAS_QE_BIT*/,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
|
||||
SPINAND_INFO("MX35LF2G14AC",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x20),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF4G24AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb5),
|
||||
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),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF4GE4AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7),
|
||||
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_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF2G14AC",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF2G24AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa4),
|
||||
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_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF2GE4AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF2GE4AC",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF1G14AC",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF1G24AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x94),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF1GE4AD",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35UF1GE4AC",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
|
||||
};
|
||||
|
||||
static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = {
|
||||
|
@ -11,6 +11,7 @@
|
||||
#define __LINUX_MTD_ONFI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
/* ONFI version bits */
|
||||
#define ONFI_VERSION_1_0 BIT(1)
|
||||
@ -24,17 +25,22 @@
|
||||
#define ONFI_VERSION_4_0 BIT(9)
|
||||
|
||||
/* ONFI features */
|
||||
#define ONFI_FEATURE_16_BIT_BUS (1 << 0)
|
||||
#define ONFI_FEATURE_EXT_PARAM_PAGE (1 << 7)
|
||||
#define ONFI_FEATURE_16_BIT_BUS BIT(0)
|
||||
#define ONFI_FEATURE_NV_DDR BIT(5)
|
||||
#define ONFI_FEATURE_EXT_PARAM_PAGE BIT(7)
|
||||
|
||||
/* ONFI timing mode, used in both asynchronous and synchronous mode */
|
||||
#define ONFI_TIMING_MODE_0 (1 << 0)
|
||||
#define ONFI_TIMING_MODE_1 (1 << 1)
|
||||
#define ONFI_TIMING_MODE_2 (1 << 2)
|
||||
#define ONFI_TIMING_MODE_3 (1 << 3)
|
||||
#define ONFI_TIMING_MODE_4 (1 << 4)
|
||||
#define ONFI_TIMING_MODE_5 (1 << 5)
|
||||
#define ONFI_TIMING_MODE_UNKNOWN (1 << 6)
|
||||
#define ONFI_DATA_INTERFACE_SDR 0
|
||||
#define ONFI_DATA_INTERFACE_NVDDR BIT(4)
|
||||
#define ONFI_DATA_INTERFACE_NVDDR2 BIT(5)
|
||||
#define ONFI_TIMING_MODE_0 BIT(0)
|
||||
#define ONFI_TIMING_MODE_1 BIT(1)
|
||||
#define ONFI_TIMING_MODE_2 BIT(2)
|
||||
#define ONFI_TIMING_MODE_3 BIT(3)
|
||||
#define ONFI_TIMING_MODE_4 BIT(4)
|
||||
#define ONFI_TIMING_MODE_5 BIT(5)
|
||||
#define ONFI_TIMING_MODE_UNKNOWN BIT(6)
|
||||
#define ONFI_TIMING_MODE_PARAM(x) FIELD_GET(GENMASK(3, 0), (x))
|
||||
|
||||
/* ONFI feature number/address */
|
||||
#define ONFI_FEATURE_NUMBER 256
|
||||
@ -49,7 +55,7 @@
|
||||
#define ONFI_SUBFEATURE_PARAM_LEN 4
|
||||
|
||||
/* ONFI optional commands SET/GET FEATURES supported? */
|
||||
#define ONFI_OPT_CMD_SET_GET_FEATURES (1 << 2)
|
||||
#define ONFI_OPT_CMD_SET_GET_FEATURES BIT(2)
|
||||
|
||||
struct nand_onfi_params {
|
||||
/* rev info and features block */
|
||||
@ -93,14 +99,15 @@ struct nand_onfi_params {
|
||||
|
||||
/* electrical parameter block */
|
||||
u8 io_pin_capacitance_max;
|
||||
__le16 async_timing_mode;
|
||||
__le16 sdr_timing_modes;
|
||||
__le16 program_cache_timing_mode;
|
||||
__le16 t_prog;
|
||||
__le16 t_bers;
|
||||
__le16 t_r;
|
||||
__le16 t_ccs;
|
||||
__le16 src_sync_timing_mode;
|
||||
u8 src_ssync_features;
|
||||
u8 nvddr_timing_modes;
|
||||
u8 nvddr2_timing_modes;
|
||||
u8 nvddr_nvddr2_features;
|
||||
__le16 clk_pin_capacitance_typ;
|
||||
__le16 io_pin_capacitance_typ;
|
||||
__le16 input_pin_capacitance_typ;
|
||||
@ -160,7 +167,9 @@ struct onfi_ext_param_page {
|
||||
* @tBERS: Block erase time
|
||||
* @tR: Page read time
|
||||
* @tCCS: Change column setup time
|
||||
* @async_timing_mode: Supported asynchronous timing mode
|
||||
* @fast_tCAD: Command/Address/Data slow or fast delay (NV-DDR only)
|
||||
* @sdr_timing_modes: Supported asynchronous/SDR timing modes
|
||||
* @nvddr_timing_modes: Supported source synchronous/NV-DDR timing modes
|
||||
* @vendor_revision: Vendor specific revision number
|
||||
* @vendor: Vendor specific data
|
||||
*/
|
||||
@ -170,7 +179,9 @@ struct onfi_params {
|
||||
u16 tBERS;
|
||||
u16 tR;
|
||||
u16 tCCS;
|
||||
u16 async_timing_mode;
|
||||
bool fast_tCAD;
|
||||
u16 sdr_timing_modes;
|
||||
u16 nvddr_timing_modes;
|
||||
u16 vendor_revision;
|
||||
u8 vendor[88];
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/types.h>
|
||||
|
||||
struct nand_chip;
|
||||
struct gpio_desc;
|
||||
|
||||
/* The maximum number of NAND chips in an array */
|
||||
#define NAND_MAX_CHIPS 8
|
||||
@ -385,8 +386,8 @@ struct nand_ecc_ctrl {
|
||||
* This struct defines the timing requirements of a SDR NAND chip.
|
||||
* These information can be found in every NAND datasheets and the timings
|
||||
* meaning are described in the ONFI specifications:
|
||||
* www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
|
||||
* Parameters)
|
||||
* https://media-www.micron.com/-/media/client/onfi/specs/onfi_3_1_spec.pdf
|
||||
* (chapter 4.15 Timing Parameters)
|
||||
*
|
||||
* All these timings are expressed in picoseconds.
|
||||
*
|
||||
@ -471,12 +472,128 @@ struct nand_sdr_timings {
|
||||
u32 tWW_min;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_nvddr_timings - NV-DDR NAND chip timings
|
||||
*
|
||||
* This struct defines the timing requirements of a NV-DDR NAND data interface.
|
||||
* These information can be found in every NAND datasheets and the timings
|
||||
* meaning are described in the ONFI specifications:
|
||||
* https://media-www.micron.com/-/media/client/onfi/specs/onfi_4_1_gold.pdf
|
||||
* (chapter 4.18.2 NV-DDR)
|
||||
*
|
||||
* All these timings are expressed in picoseconds.
|
||||
*
|
||||
* @tBERS_max: Block erase time
|
||||
* @tCCS_min: Change column setup time
|
||||
* @tPROG_max: Page program time
|
||||
* @tR_max: Page read time
|
||||
* @tAC_min: Access window of DQ[7:0] from CLK
|
||||
* @tAC_max: Access window of DQ[7:0] from CLK
|
||||
* @tADL_min: ALE to data loading time
|
||||
* @tCAD_min: Command, Address, Data delay
|
||||
* @tCAH_min: Command/Address DQ hold time
|
||||
* @tCALH_min: W/R_n, CLE and ALE hold time
|
||||
* @tCALS_min: W/R_n, CLE and ALE setup time
|
||||
* @tCAS_min: Command/address DQ setup time
|
||||
* @tCEH_min: CE# high hold time
|
||||
* @tCH_min: CE# hold time
|
||||
* @tCK_min: Average clock cycle time
|
||||
* @tCS_min: CE# setup time
|
||||
* @tDH_min: Data hold time
|
||||
* @tDQSCK_min: Start of the access window of DQS from CLK
|
||||
* @tDQSCK_max: End of the access window of DQS from CLK
|
||||
* @tDQSD_min: Min W/R_n low to DQS/DQ driven by device
|
||||
* @tDQSD_max: Max W/R_n low to DQS/DQ driven by device
|
||||
* @tDQSHZ_max: W/R_n high to DQS/DQ tri-state by device
|
||||
* @tDQSQ_max: DQS-DQ skew, DQS to last DQ valid, per access
|
||||
* @tDS_min: Data setup time
|
||||
* @tDSC_min: DQS cycle time
|
||||
* @tFEAT_max: Busy time for Set Features and Get Features
|
||||
* @tITC_max: Interface and Timing Mode Change time
|
||||
* @tQHS_max: Data hold skew factor
|
||||
* @tRHW_min: Data output cycle to command, address, or data input cycle
|
||||
* @tRR_min: Ready to RE# low (data only)
|
||||
* @tRST_max: Device reset time, measured from the falling edge of R/B# to the
|
||||
* rising edge of R/B#.
|
||||
* @tWB_max: WE# high to SR[6] low
|
||||
* @tWHR_min: WE# high to RE# low
|
||||
* @tWRCK_min: W/R_n low to data output cycle
|
||||
* @tWW_min: WP# transition to WE# low
|
||||
*/
|
||||
struct nand_nvddr_timings {
|
||||
u64 tBERS_max;
|
||||
u32 tCCS_min;
|
||||
u64 tPROG_max;
|
||||
u64 tR_max;
|
||||
u32 tAC_min;
|
||||
u32 tAC_max;
|
||||
u32 tADL_min;
|
||||
u32 tCAD_min;
|
||||
u32 tCAH_min;
|
||||
u32 tCALH_min;
|
||||
u32 tCALS_min;
|
||||
u32 tCAS_min;
|
||||
u32 tCEH_min;
|
||||
u32 tCH_min;
|
||||
u32 tCK_min;
|
||||
u32 tCS_min;
|
||||
u32 tDH_min;
|
||||
u32 tDQSCK_min;
|
||||
u32 tDQSCK_max;
|
||||
u32 tDQSD_min;
|
||||
u32 tDQSD_max;
|
||||
u32 tDQSHZ_max;
|
||||
u32 tDQSQ_max;
|
||||
u32 tDS_min;
|
||||
u32 tDSC_min;
|
||||
u32 tFEAT_max;
|
||||
u32 tITC_max;
|
||||
u32 tQHS_max;
|
||||
u32 tRHW_min;
|
||||
u32 tRR_min;
|
||||
u32 tRST_max;
|
||||
u32 tWB_max;
|
||||
u32 tWHR_min;
|
||||
u32 tWRCK_min;
|
||||
u32 tWW_min;
|
||||
};
|
||||
|
||||
/*
|
||||
* While timings related to the data interface itself are mostly different
|
||||
* between SDR and NV-DDR, timings related to the internal chip behavior are
|
||||
* common. IOW, the following entries which describe the internal delays have
|
||||
* the same definition and are shared in both SDR and NV-DDR timing structures:
|
||||
* - tADL_min
|
||||
* - tBERS_max
|
||||
* - tCCS_min
|
||||
* - tFEAT_max
|
||||
* - tPROG_max
|
||||
* - tR_max
|
||||
* - tRR_min
|
||||
* - tRST_max
|
||||
* - tWB_max
|
||||
*
|
||||
* The below macros return the value of a given timing, no matter the interface.
|
||||
*/
|
||||
#define NAND_COMMON_TIMING_PS(conf, timing_name) \
|
||||
nand_interface_is_sdr(conf) ? \
|
||||
nand_get_sdr_timings(conf)->timing_name : \
|
||||
nand_get_nvddr_timings(conf)->timing_name
|
||||
|
||||
#define NAND_COMMON_TIMING_MS(conf, timing_name) \
|
||||
PSEC_TO_MSEC(NAND_COMMON_TIMING_PS((conf), timing_name))
|
||||
|
||||
#define NAND_COMMON_TIMING_NS(conf, timing_name) \
|
||||
PSEC_TO_NSEC(NAND_COMMON_TIMING_PS((conf), timing_name))
|
||||
|
||||
/**
|
||||
* enum nand_interface_type - NAND interface type
|
||||
* @NAND_SDR_IFACE: Single Data Rate interface
|
||||
* @NAND_NVDDR_IFACE: Double Data Rate interface
|
||||
*/
|
||||
enum nand_interface_type {
|
||||
NAND_SDR_IFACE,
|
||||
NAND_NVDDR_IFACE,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -485,6 +602,7 @@ enum nand_interface_type {
|
||||
* @timings: The timing information
|
||||
* @timings.mode: Timing mode as defined in the specification
|
||||
* @timings.sdr: Use it when @type is %NAND_SDR_IFACE.
|
||||
* @timings.nvddr: Use it when @type is %NAND_NVDDR_IFACE.
|
||||
*/
|
||||
struct nand_interface_config {
|
||||
enum nand_interface_type type;
|
||||
@ -492,10 +610,29 @@ struct nand_interface_config {
|
||||
unsigned int mode;
|
||||
union {
|
||||
struct nand_sdr_timings sdr;
|
||||
struct nand_nvddr_timings nvddr;
|
||||
};
|
||||
} timings;
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_interface_is_sdr - get the interface type
|
||||
* @conf: The data interface
|
||||
*/
|
||||
static bool nand_interface_is_sdr(const struct nand_interface_config *conf)
|
||||
{
|
||||
return conf->type == NAND_SDR_IFACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_interface_is_nvddr - get the interface type
|
||||
* @conf: The data interface
|
||||
*/
|
||||
static bool nand_interface_is_nvddr(const struct nand_interface_config *conf)
|
||||
{
|
||||
return conf->type == NAND_NVDDR_IFACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_get_sdr_timings - get SDR timing from data interface
|
||||
* @conf: The data interface
|
||||
@ -503,12 +640,25 @@ struct nand_interface_config {
|
||||
static inline const struct nand_sdr_timings *
|
||||
nand_get_sdr_timings(const struct nand_interface_config *conf)
|
||||
{
|
||||
if (conf->type != NAND_SDR_IFACE)
|
||||
if (!nand_interface_is_sdr(conf))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return &conf->timings.sdr;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_get_nvddr_timings - get NV-DDR timing from data interface
|
||||
* @conf: The data interface
|
||||
*/
|
||||
static inline const struct nand_nvddr_timings *
|
||||
nand_get_nvddr_timings(const struct nand_interface_config *conf)
|
||||
{
|
||||
if (!nand_interface_is_nvddr(conf))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return &conf->timings.nvddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct nand_op_cmd_instr - Definition of a command instruction
|
||||
* @opcode: the command to issue in one cycle
|
||||
@ -1413,7 +1563,6 @@ void nand_cleanup(struct nand_chip *chip);
|
||||
* instruction and have no physical pin to check it.
|
||||
*/
|
||||
int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms);
|
||||
struct gpio_desc;
|
||||
int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
|
||||
unsigned long timeout_ms);
|
||||
|
||||
@ -1446,4 +1595,8 @@ static inline void *nand_get_data_buf(struct nand_chip *chip)
|
||||
return chip->data_buf;
|
||||
}
|
||||
|
||||
/* Parse the gpio-cs property */
|
||||
int rawnand_dt_parse_gpio_cs(struct device *dev, struct gpio_desc ***cs_array,
|
||||
unsigned int *ncs_array);
|
||||
|
||||
#endif /* __LINUX_MTD_RAWNAND_H */
|
||||
|
@ -1,30 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* ARM PL353 SMC Driver Header
|
||||
*
|
||||
* Copyright (C) 2012 - 2018 Xilinx, Inc
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_PL353_SMC_H
|
||||
#define __LINUX_PL353_SMC_H
|
||||
|
||||
enum pl353_smc_ecc_mode {
|
||||
PL353_SMC_ECCMODE_BYPASS = 0,
|
||||
PL353_SMC_ECCMODE_APB = 1,
|
||||
PL353_SMC_ECCMODE_MEM = 2
|
||||
};
|
||||
|
||||
enum pl353_smc_mem_width {
|
||||
PL353_SMC_MEM_WIDTH_8 = 0,
|
||||
PL353_SMC_MEM_WIDTH_16 = 1
|
||||
};
|
||||
|
||||
u32 pl353_smc_get_ecc_val(int ecc_reg);
|
||||
bool pl353_smc_ecc_is_busy(void);
|
||||
int pl353_smc_get_nand_int_status_raw(void);
|
||||
void pl353_smc_clr_nand_int(void);
|
||||
int pl353_smc_set_ecc_mode(enum pl353_smc_ecc_mode mode);
|
||||
int pl353_smc_set_ecc_pg_size(unsigned int pg_sz);
|
||||
int pl353_smc_set_buswidth(unsigned int bw);
|
||||
void pl353_smc_set_cycles(u32 timings[]);
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user