MTD core changes:
* Spelling * http to https updates NAND core changes: * Drop useless 'depends on' in Kconfig * Add an extra level in the Kconfig hierarchy * Trivial spellings * Dynamic allocation of the interface configurations * Dropping the default ONFI timing mode * Various cleanup (types, structures, naming, comments) * Hide the chip->data_interface indirection * Add the generic rb-gpios property * Add the ->choose_interface_config() hook * Introduce nand_choose_best_sdr_timings() * Use default values for tPROG_max and tBERS_max * Avoid redefining tR_max and tCCS_min * Add a helper to find the closest ONFI mode * bcm63xx MTD parsers: simplify CFE detection Raw NAND controller drivers changes: * fsl-upm: Deprecation of specific DT properties * fsl_upm: Driver rework and cleanup in favor of ->exec_op() * Ingenic: Cleanup ARRAY_SIZE() vs sizeof() use * brcmnand: ECC error handling on EDU transfers * brcmnand: Don't default to EDU transfers * qcom: Set BAM mode only if not set already * qcom: Avoid write to unavailable register * gpio: Driver rework in favor of ->exec_op() * tango: ->exec_op() conversion * mtk: ->exec_op() conversion Raw NAND chip drivers changes: * toshiba: Implement ->choose_interface_config() for TH58NVG2S3HBAI4 * toshiba: Implement ->choose_interface_config() for TC58NVG0S3E * toshiba: Implement ->choose_interface_config() for TC58TEG5DCLTA00 * hynix: Implement ->choose_interface_config() for H27UCG8T2ATR-BC SPI NOR core changes: * Disable Quad Mode in spi_nor_restore(). * Don't abort BFPT parsing when QER reserved value is used. * Add support/update capabilities for few flashes. * Drop s70fl01gs flash: it does not support RDSR(05h) which is critical for erase/write. * Merge the SPIMEM DTR bits in spi-nor/next to avoid conflicts during the release cycle. SPI NOR controller drivers changes: * Move the cadence-quadspi driver to spi-mem. The series was taken through the SPI tree. Merge it also in spi-nor/next to avoid conflicts during the release cycle. * intel-spi: - Add new PCI IDs. - Ignore the Write Disable command, the controller doesn't support it. - Fix performance regression. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAl8vJtMACgkQJWrqGEe9 VoRdGAf/Y5m5BwmLilkEYpffyxi7dVR6XOKPLU5EJXkS3dPvH9398zchbHOdedCZ OzJIfh6Iv+qbkgS2g0lAAT+SAfOfG9plubvSdkjrHXl4eZXRnR/49RF5LAEju7sz Uw1HdRcawyEi5uI9yYS0tCeVMIUJq+5x7VibH+82yOIdSPc60c7FDc5ih/nVKj/a Pn9LOzGzkdndcE1b3FcF2Uk/T1YOJx3Yt5ngALlPpJxaDZmQSHtYPuuz8DfUbamf uj3CkpqYRyT18CzuFvtuba6LyF+donXNJgvl6ivW7dlRSPzSMnDQu7J5bpNhUfcd p/ZdzX1Jxle4theDm0J9ALsSSM5g2w== =RiY8 -----END PGP SIGNATURE----- Merge tag 'mtd/for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux Pull mtd updates from Miquel Raynal: "MTD core changes: - Spelling - http to https updates NAND core changes: - Drop useless 'depends on' in Kconfig - Add an extra level in the Kconfig hierarchy - Trivial spellings - Dynamic allocation of the interface configurations - Dropping the default ONFI timing mode - Various cleanup (types, structures, naming, comments) - Hide the chip->data_interface indirection - Add the generic rb-gpios property - Add the ->choose_interface_config() hook - Introduce nand_choose_best_sdr_timings() - Use default values for tPROG_max and tBERS_max - Avoid redefining tR_max and tCCS_min - Add a helper to find the closest ONFI mode - bcm63xx MTD parsers: simplify CFE detection Raw NAND controller drivers changes: - fsl-upm: Deprecation of specific DT properties - fsl_upm: Driver rework and cleanup in favor of ->exec_op() - Ingenic: Cleanup ARRAY_SIZE() vs sizeof() use - brcmnand: ECC error handling on EDU transfers - brcmnand: Don't default to EDU transfers - qcom: Set BAM mode only if not set already - qcom: Avoid write to unavailable register - gpio: Driver rework in favor of ->exec_op() - tango: ->exec_op() conversion - mtk: ->exec_op() conversion Raw NAND chip drivers changes: - toshiba: Implement ->choose_interface_config() for TH58NVG2S3HBAI4, TC58NVG0S3E, and TC58TEG5DCLTA00 - hynix: Implement ->choose_interface_config() for H27UCG8T2ATR-BC SPI NOR core changes: - Disable Quad Mode in spi_nor_restore(). - Don't abort BFPT parsing when QER reserved value is used. - Add support/update capabilities for few flashes. - Drop s70fl01gs flash: it does not support RDSR(05h) which is critical for erase/write. - Merge the SPIMEM DTR bits in spi-nor/next to avoid conflicts during the release cycle. SPI NOR controller drivers changes: - Move the cadence-quadspi driver to spi-mem. The series was taken through the SPI tree. Merge it also in spi-nor/next to avoid conflicts during the release cycle. - intel-spi: - Add new PCI IDs. - Ignore the Write Disable command, the controller doesn't support it. - Fix performance regression" * tag 'mtd/for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (79 commits) MTD: pfow.h: drop a duplicated word MTD: mtd-abi.h: drop a duplicated word mtd: rawnand: omap_elm: Replace HTTP links with HTTPS ones mtd: Replace HTTP links with HTTPS ones mtd: hyperbus: Replace HTTP links with HTTPS ones mtd: revert "spi-nor: intel: provide a range for poll_timout" mtd: spi-nor: update read capabilities for w25q64 and s25fl064k mtd: spi-nor: micron: Add SPI_NOR_DUAL_READ flag on mt25qu02g mtd: spi-nor: macronix: Add support for mx66u2g45g mtd: spi-nor: intel-spi: Simulate WRDI command mtd: spi-nor: Disable the flash quad mode in spi_nor_restore() mtd: spi-nor: Add capability to disable flash quad mode mtd: spi-nor: spansion: Remove s70fl01gs from flash_info mtd: spi-nor: sfdp: do not make invalid quad enable fatal dt-bindings: mtd: fsl-upm-nand: Deprecate chip-delay and fsl, upm-wait-flags mtd: rawnand: stm32_fmc2: get resources from parent node mtd: rawnand: stm32_fmc2: use regmap APIs memory: stm32-fmc2-ebi: add STM32 FMC2 EBI controller driver dt-bindings: memory-controller: add STM32 FMC2 EBI controller documentation dt-bindings: mtd: update STM32 FMC2 NAND controller documentation ...
This commit is contained in:
commit
dec1fbbc1d
@ -0,0 +1,252 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/st,stm32-fmc2-ebi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: STMicroelectronics Flexible Memory Controller 2 (FMC2) Bindings
|
||||
|
||||
description: |
|
||||
The FMC2 functional block makes the interface with: synchronous and
|
||||
asynchronous static devices (such as PSNOR, PSRAM or other memory-mapped
|
||||
peripherals) and NAND flash memories.
|
||||
Its main purposes are:
|
||||
- to translate AXI transactions into the appropriate external device
|
||||
protocol
|
||||
- to meet the access time requirements of the external devices
|
||||
All external devices share the addresses, data and control signals with the
|
||||
controller. Each external device is accessed by means of a unique Chip
|
||||
Select. The FMC2 performs only one access at a time to an external device.
|
||||
|
||||
maintainers:
|
||||
- Christophe Kerello <christophe.kerello@st.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32mp1-fmc2-ebi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 2
|
||||
|
||||
"#size-cells":
|
||||
const: 1
|
||||
|
||||
ranges:
|
||||
description: |
|
||||
Reflects the memory layout with four integer values per bank. Format:
|
||||
<bank-number> 0 <address of the bank> <size>
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-4],[a-f0-9]+$":
|
||||
type: object
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: Bank number, base address and size of the device.
|
||||
|
||||
st,fmc2-ebi-cs-transaction-type:
|
||||
description: |
|
||||
Select one of the transactions type supported
|
||||
0: Asynchronous mode 1 SRAM/FRAM.
|
||||
1: Asynchronous mode 1 PSRAM.
|
||||
2: Asynchronous mode A SRAM/FRAM.
|
||||
3: Asynchronous mode A PSRAM.
|
||||
4: Asynchronous mode 2 NOR.
|
||||
5: Asynchronous mode B NOR.
|
||||
6: Asynchronous mode C NOR.
|
||||
7: Asynchronous mode D NOR.
|
||||
8: Synchronous read synchronous write PSRAM.
|
||||
9: Synchronous read asynchronous write PSRAM.
|
||||
10: Synchronous read synchronous write NOR.
|
||||
11: Synchronous read asynchronous write NOR.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 11
|
||||
|
||||
st,fmc2-ebi-cs-cclk-enable:
|
||||
description: Continuous clock enable (first bank must be configured
|
||||
in synchronous mode). The FMC_CLK is generated continuously
|
||||
during asynchronous and synchronous access. By default, the
|
||||
FMC_CLK is only generated during synchronous access.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
st,fmc2-ebi-cs-mux-enable:
|
||||
description: Address/Data multiplexed on databus (valid only with
|
||||
NOR and PSRAM transactions type). By default, Address/Data
|
||||
are not multiplexed.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
st,fmc2-ebi-cs-buswidth:
|
||||
description: Data bus width
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [ 8, 16 ]
|
||||
default: 16
|
||||
|
||||
st,fmc2-ebi-cs-waitpol-high:
|
||||
description: Wait signal polarity (NWAIT signal active high).
|
||||
By default, NWAIT is active low.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
st,fmc2-ebi-cs-waitcfg-enable:
|
||||
description: The NWAIT signal indicates wheither the data from the
|
||||
device are valid or if a wait state must be inserted when accessing
|
||||
the device in synchronous mode. By default, the NWAIT signal is
|
||||
active one data cycle before wait state.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
st,fmc2-ebi-cs-wait-enable:
|
||||
description: The NWAIT signal is enabled (its level is taken into
|
||||
account after the programmed latency period to insert wait states
|
||||
if asserted). By default, the NWAIT signal is disabled.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
st,fmc2-ebi-cs-asyncwait-enable:
|
||||
description: The NWAIT signal is taken into account during asynchronous
|
||||
transactions. By default, the NWAIT signal is not taken into account
|
||||
during asynchronous transactions.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
st,fmc2-ebi-cs-cpsize:
|
||||
description: CRAM page size. The controller splits the burst access
|
||||
when the memory page is reached. By default, no burst split when
|
||||
crossing page boundary.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [ 0, 128, 256, 512, 1024 ]
|
||||
default: 0
|
||||
|
||||
st,fmc2-ebi-cs-byte-lane-setup-ns:
|
||||
description: This property configures the byte lane setup timing
|
||||
defined in nanoseconds from NBLx low to Chip Select NEx low.
|
||||
|
||||
st,fmc2-ebi-cs-address-setup-ns:
|
||||
description: This property defines the duration of the address setup
|
||||
phase in nanoseconds used for asynchronous read/write transactions.
|
||||
|
||||
st,fmc2-ebi-cs-address-hold-ns:
|
||||
description: This property defines the duration of the address hold
|
||||
phase in nanoseconds used for asynchronous multiplexed read/write
|
||||
transactions.
|
||||
|
||||
st,fmc2-ebi-cs-data-setup-ns:
|
||||
description: This property defines the duration of the data setup phase
|
||||
in nanoseconds used for asynchronous read/write transactions.
|
||||
|
||||
st,fmc2-ebi-cs-bus-turnaround-ns:
|
||||
description: This property defines the delay in nanoseconds between the
|
||||
end of current read/write transaction and the next transaction.
|
||||
|
||||
st,fmc2-ebi-cs-data-hold-ns:
|
||||
description: This property defines the duration of the data hold phase
|
||||
in nanoseconds used for asynchronous read/write transactions.
|
||||
|
||||
st,fmc2-ebi-cs-clk-period-ns:
|
||||
description: This property defines the FMC_CLK output signal period in
|
||||
nanoseconds.
|
||||
|
||||
st,fmc2-ebi-cs-data-latency-ns:
|
||||
description: This property defines the data latency before reading or
|
||||
writing the first data in nanoseconds.
|
||||
|
||||
st,fmc2_ebi-cs-write-address-setup-ns:
|
||||
description: This property defines the duration of the address setup
|
||||
phase in nanoseconds used for asynchronous write transactions.
|
||||
|
||||
st,fmc2-ebi-cs-write-address-hold-ns:
|
||||
description: This property defines the duration of the address hold
|
||||
phase in nanoseconds used for asynchronous multiplexed write
|
||||
transactions.
|
||||
|
||||
st,fmc2-ebi-cs-write-data-setup-ns:
|
||||
description: This property defines the duration of the data setup
|
||||
phase in nanoseconds used for asynchronous write transactions.
|
||||
|
||||
st,fmc2-ebi-cs-write-bus-turnaround-ns:
|
||||
description: This property defines the delay between the end of current
|
||||
write transaction and the next transaction in nanoseconds.
|
||||
|
||||
st,fmc2-ebi-cs-write-data-hold-ns:
|
||||
description: This property defines the duration of the data hold phase
|
||||
in nanoseconds used for asynchronous write transactions.
|
||||
|
||||
st,fmc2-ebi-cs-max-low-pulse-ns:
|
||||
description: This property defines the maximum chip select low pulse
|
||||
duration in nanoseconds for synchronous transactions. When this timing
|
||||
reaches 0, the controller splits the current access, toggles NE to
|
||||
allow device refresh and restarts a new access.
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- ranges
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/stm32mp1-clks.h>
|
||||
#include <dt-bindings/reset/stm32mp1-resets.h>
|
||||
memory-controller@58002000 {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
compatible = "st,stm32mp1-fmc2-ebi";
|
||||
reg = <0x58002000 0x1000>;
|
||||
clocks = <&rcc FMC_K>;
|
||||
resets = <&rcc FMC_R>;
|
||||
|
||||
ranges = <0 0 0x60000000 0x04000000>, /* EBI CS 1 */
|
||||
<1 0 0x64000000 0x04000000>, /* EBI CS 2 */
|
||||
<2 0 0x68000000 0x04000000>, /* EBI CS 3 */
|
||||
<3 0 0x6c000000 0x04000000>, /* EBI CS 4 */
|
||||
<4 0 0x80000000 0x10000000>; /* NAND */
|
||||
|
||||
psram@0,0 {
|
||||
compatible = "mtd-ram";
|
||||
reg = <0 0x00000000 0x100000>;
|
||||
bank-width = <2>;
|
||||
|
||||
st,fmc2-ebi-cs-transaction-type = <1>;
|
||||
st,fmc2-ebi-cs-address-setup-ns = <60>;
|
||||
st,fmc2-ebi-cs-data-setup-ns = <30>;
|
||||
st,fmc2-ebi-cs-bus-turnaround-ns = <5>;
|
||||
};
|
||||
|
||||
nand-controller@4,0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "st,stm32mp1-fmc2-nfc";
|
||||
reg = <4 0x00000000 0x1000>,
|
||||
<4 0x08010000 0x1000>,
|
||||
<4 0x08020000 0x1000>,
|
||||
<4 0x01000000 0x1000>,
|
||||
<4 0x09010000 0x1000>,
|
||||
<4 0x09020000 0x1000>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdma1 20 0x2 0x12000a02 0x0 0x0>,
|
||||
<&mdma1 20 0x2 0x12000a08 0x0 0x0>,
|
||||
<&mdma1 21 0x2 0x12000a0a 0x0 0x0>;
|
||||
dma-names = "tx", "rx", "ecc";
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-on-flash-bbt;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -4,8 +4,8 @@ This file provides information, what the device node for the davinci/keystone
|
||||
NAND interface contains.
|
||||
|
||||
Documentation:
|
||||
Davinci DM646x - http://www.ti.com/lit/ug/sprueq7c/sprueq7c.pdf
|
||||
Kestone - http://www.ti.com/lit/ug/sprugz3a/sprugz3a.pdf
|
||||
Davinci DM646x - https://www.ti.com/lit/ug/sprueq7c/sprueq7c.pdf
|
||||
Kestone - https://www.ti.com/lit/ug/sprugz3a/sprugz3a.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
|
@ -7,14 +7,16 @@ Required properties:
|
||||
- fsl,upm-cmd-offset : UPM pattern offset for the command latch.
|
||||
|
||||
Optional properties:
|
||||
- fsl,upm-wait-flags : add chip-dependent short delays after running the
|
||||
UPM pattern (0x1), after writing a data byte (0x2) or after
|
||||
writing out a buffer (0x4).
|
||||
- fsl,upm-addr-line-cs-offsets : address offsets for multi-chip support.
|
||||
The corresponding address lines are used to select the chip.
|
||||
- gpios : may specify optional GPIOs connected to the Ready-Not-Busy pins
|
||||
(R/B#). For multi-chip devices, "n" GPIO definitions are required
|
||||
according to the number of chips.
|
||||
|
||||
Deprecated properties:
|
||||
- fsl,upm-wait-flags : add chip-dependent short delays after running the
|
||||
UPM pattern (0x1), after writing a data byte (0x2) or after
|
||||
writing out a buffer (0x4).
|
||||
- chip-delay : chip dependent delay for transferring data from array to
|
||||
read registers (tR). Required if property "gpios" is not used
|
||||
(R/B# pins not connected).
|
||||
@ -52,8 +54,6 @@ upm@3,0 {
|
||||
fsl,upm-cmd-offset = <0x08>;
|
||||
/* Multi-chip NAND device */
|
||||
fsl,upm-addr-line-cs-offsets = <0x0 0x200>;
|
||||
fsl,upm-wait-flags = <0x5>;
|
||||
chip-delay = <25>; // in micro-seconds
|
||||
|
||||
nand@0 {
|
||||
#address-cells = <1>;
|
||||
|
@ -114,6 +114,13 @@ patternProperties:
|
||||
description:
|
||||
Contains the native Ready/Busy IDs.
|
||||
|
||||
rb-gpios:
|
||||
description:
|
||||
Contains one or more GPIO descriptor (the numper of descriptor
|
||||
depends on the number of R/B pins exposed by the flash) for the
|
||||
Ready/Busy pins. Active state refers to the NAND ready state and
|
||||
should be set to GPIOD_ACTIVE_HIGH unless the signal is inverted.
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
|
@ -9,32 +9,19 @@ title: STMicroelectronics Flexible Memory Controller 2 (FMC2) Bindings
|
||||
maintainers:
|
||||
- Christophe Kerello <christophe.kerello@st.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "nand-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32mp15-fmc2
|
||||
enum:
|
||||
- st,stm32mp15-fmc2
|
||||
- st,stm32mp1-fmc2-nfc
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: Registers
|
||||
- description: Chip select 0 data
|
||||
- description: Chip select 0 command
|
||||
- description: Chip select 0 address space
|
||||
- description: Chip select 1 data
|
||||
- description: Chip select 1 command
|
||||
- description: Chip select 1 address space
|
||||
minItems: 6
|
||||
maxItems: 7
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: tx DMA channel
|
||||
@ -57,11 +44,55 @@ patternProperties:
|
||||
nand-ecc-strength:
|
||||
enum: [1, 4 ,8 ]
|
||||
|
||||
allOf:
|
||||
- $ref: "nand-controller.yaml#"
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: st,stm32mp15-fmc2
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Registers
|
||||
- description: Chip select 0 data
|
||||
- description: Chip select 0 command
|
||||
- description: Chip select 0 address space
|
||||
- description: Chip select 1 data
|
||||
- description: Chip select 1 command
|
||||
- description: Chip select 1 address space
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- clocks
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: st,stm32mp1-fmc2-nfc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Chip select 0 data
|
||||
- description: Chip select 0 command
|
||||
- description: Chip select 0 address space
|
||||
- description: Chip select 1 data
|
||||
- description: Chip select 1 command
|
||||
- description: Chip select 1 address space
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
examples:
|
||||
- |
|
||||
@ -77,13 +108,13 @@ examples:
|
||||
<0x81000000 0x1000>,
|
||||
<0x89010000 0x1000>,
|
||||
<0x89020000 0x1000>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdma1 20 0x10 0x12000a02 0x0 0x0>,
|
||||
<&mdma1 20 0x10 0x12000a08 0x0 0x0>,
|
||||
<&mdma1 21 0x10 0x12000a0a 0x0 0x0>;
|
||||
dma-names = "tx", "rx", "ecc";
|
||||
clocks = <&rcc FMC_K>;
|
||||
resets = <&rcc FMC_R>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&mdma1 20 0x2 0x12000a02 0x0 0x0>,
|
||||
<&mdma1 20 0x2 0x12000a08 0x0 0x0>,
|
||||
<&mdma1 21 0x2 0x12000a0a 0x0 0x0>;
|
||||
dma-names = "tx", "rx", "ecc";
|
||||
clocks = <&rcc FMC_K>;
|
||||
resets = <&rcc FMC_R>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
|
@ -188,6 +188,16 @@ config RENESAS_RPCIF
|
||||
host or HyperFlash. You'll have to select individual components
|
||||
under the corresponding menu.
|
||||
|
||||
config STM32_FMC2_EBI
|
||||
tristate "Support for FMC2 External Bus Interface on STM32MP SoCs"
|
||||
depends on MACH_STM32MP157 || COMPILE_TEST
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Select this option to enable the STM32 FMC2 External Bus Interface
|
||||
controller. This driver configures the transactions with external
|
||||
devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
|
||||
SOCs containing the FMC2 External Bus Interface.
|
||||
|
||||
source "drivers/memory/samsung/Kconfig"
|
||||
source "drivers/memory/tegra/Kconfig"
|
||||
|
||||
|
@ -23,6 +23,7 @@ obj-$(CONFIG_MTK_SMI) += mtk-smi.o
|
||||
obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o
|
||||
obj-$(CONFIG_PL353_SMC) += pl353-smc.o
|
||||
obj-$(CONFIG_RENESAS_RPCIF) += renesas-rpc-if.o
|
||||
obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o
|
||||
|
||||
obj-$(CONFIG_SAMSUNG_MC) += samsung/
|
||||
obj-$(CONFIG_TEGRA_MC) += tegra/
|
||||
|
1206
drivers/memory/stm32-fmc2-ebi.c
Normal file
1206
drivers/memory/stm32-fmc2-ebi.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ config MTD_CFI
|
||||
AMD and other flash manufactures that provides a universal method
|
||||
for probing the capabilities of flash devices. If you wish to
|
||||
support any device that is CFI-compliant, you need to enable this
|
||||
option. Visit <http://www.amd.com/products/nvd/overview/cfi.html>
|
||||
option. Visit <https://www.amd.com/products/nvd/overview/cfi.html>
|
||||
for more information on CFI.
|
||||
|
||||
config MTD_JEDECPROBE
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
// Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/
|
||||
// Author: Vignesh Raghavendra <vigneshr@ti.com>
|
||||
|
||||
#include <linux/err.h>
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
// Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/
|
||||
// Author: Vignesh Raghavendra <vigneshr@ti.com>
|
||||
|
||||
#include <linux/err.h>
|
||||
|
@ -310,7 +310,7 @@ config MTD_DC21285
|
||||
help
|
||||
This provides a driver for the flash accessed using Intel's
|
||||
21285 bridge used with Intel's StrongARM processors. More info at
|
||||
<http://www.intel.com/design/bridge/docs/21285_documentation.htm>.
|
||||
<https://www.intel.com/design/bridge/docs/21285_documentation.htm>.
|
||||
|
||||
config MTD_IXP4XX
|
||||
tristate "CFI Flash device mapped on Intel IXP4xx based systems"
|
||||
|
@ -6,7 +6,7 @@
|
||||
* The SC520CDP is an evaluation board for the Elan SC520 processor available
|
||||
* from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size,
|
||||
* and up to 512 KiB of 8-bit DIL Flash ROM.
|
||||
* For details see http://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html
|
||||
* For details see https://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -1,7 +1,12 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
menu "NAND"
|
||||
|
||||
config MTD_NAND_CORE
|
||||
tristate
|
||||
|
||||
source "drivers/mtd/nand/onenand/Kconfig"
|
||||
source "drivers/mtd/nand/raw/Kconfig"
|
||||
source "drivers/mtd/nand/spi/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -1,7 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menuconfig MTD_ONENAND
|
||||
tristate "OneNAND Device Support"
|
||||
depends on MTD
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables support for accessing all type of OneNAND flash
|
||||
|
@ -12,7 +12,6 @@ config MTD_NAND_ECC_SW_HAMMING_SMC
|
||||
|
||||
menuconfig MTD_RAW_NAND
|
||||
tristate "Raw/Parallel NAND Device Support"
|
||||
depends on MTD
|
||||
select MTD_NAND_CORE
|
||||
select MTD_NAND_ECC_SW_HAMMING
|
||||
help
|
||||
@ -415,6 +414,7 @@ config MTD_NAND_TEGRA
|
||||
config MTD_NAND_STM32_FMC2
|
||||
tristate "Support for NAND controller on STM32MP SoCs"
|
||||
depends on MACH_STM32MP157 || COMPILE_TEST
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enables support for NAND Flash chips on SoCs containing the FMC2
|
||||
NAND controller. This controller is found on STM32MP SoCs.
|
||||
|
@ -191,8 +191,8 @@ static int gpio_nand_exec_op(struct nand_chip *this,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_nand_setup_data_interface(struct nand_chip *this, int csline,
|
||||
const struct nand_data_interface *cf)
|
||||
static int gpio_nand_setup_interface(struct nand_chip *this, int csline,
|
||||
const struct nand_interface_config *cf)
|
||||
{
|
||||
struct gpio_nand *priv = nand_get_controller_data(this);
|
||||
const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf);
|
||||
@ -217,7 +217,7 @@ static int gpio_nand_setup_data_interface(struct nand_chip *this, int csline,
|
||||
|
||||
static const struct nand_controller_ops gpio_nand_ops = {
|
||||
.exec_op = gpio_nand_exec_op,
|
||||
.setup_data_interface = gpio_nand_setup_data_interface,
|
||||
.setup_interface = gpio_nand_setup_interface,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -854,8 +854,8 @@ static int anfc_exec_op(struct nand_chip *chip,
|
||||
return nand_op_parser_exec_op(chip, &anfc_op_parser, op, check_only);
|
||||
}
|
||||
|
||||
static int anfc_setup_data_interface(struct nand_chip *chip, int target,
|
||||
const struct nand_data_interface *conf)
|
||||
static int anfc_setup_interface(struct nand_chip *chip, int target,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct anand *anand = to_anand(chip);
|
||||
struct arasan_nfc *nfc = to_anfc(chip->controller);
|
||||
@ -1083,7 +1083,7 @@ static void anfc_detach_chip(struct nand_chip *chip)
|
||||
|
||||
static const struct nand_controller_ops anfc_ops = {
|
||||
.exec_op = anfc_exec_op,
|
||||
.setup_data_interface = anfc_setup_data_interface,
|
||||
.setup_interface = anfc_setup_interface,
|
||||
.attach_chip = anfc_attach_chip,
|
||||
.detach_chip = anfc_detach_chip,
|
||||
};
|
||||
|
@ -200,8 +200,8 @@ struct atmel_nand_controller_ops {
|
||||
void (*nand_init)(struct atmel_nand_controller *nc,
|
||||
struct atmel_nand *nand);
|
||||
int (*ecc_init)(struct nand_chip *chip);
|
||||
int (*setup_data_interface)(struct atmel_nand *nand, int csline,
|
||||
const struct nand_data_interface *conf);
|
||||
int (*setup_interface)(struct atmel_nand *nand, int csline,
|
||||
const struct nand_interface_config *conf);
|
||||
};
|
||||
|
||||
struct atmel_nand_controller_caps {
|
||||
@ -1168,7 +1168,7 @@ static int atmel_hsmc_nand_ecc_init(struct nand_chip *chip)
|
||||
}
|
||||
|
||||
static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
|
||||
const struct nand_data_interface *conf,
|
||||
const struct nand_interface_config *conf,
|
||||
struct atmel_smc_cs_conf *smcconf)
|
||||
{
|
||||
u32 ncycles, totalcycles, timeps, mckperiodps;
|
||||
@ -1397,9 +1397,9 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand,
|
||||
static int atmel_smc_nand_setup_interface(struct atmel_nand *nand,
|
||||
int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct atmel_nand_controller *nc;
|
||||
struct atmel_smc_cs_conf smcconf;
|
||||
@ -1422,9 +1422,9 @@ static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand,
|
||||
static int atmel_hsmc_nand_setup_interface(struct atmel_nand *nand,
|
||||
int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct atmel_hsmc_nand_controller *nc;
|
||||
struct atmel_smc_cs_conf smcconf;
|
||||
@ -1452,8 +1452,8 @@ static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_nand_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
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);
|
||||
struct atmel_nand_controller *nc;
|
||||
@ -1464,7 +1464,7 @@ static int atmel_nand_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
(csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
|
||||
return -EINVAL;
|
||||
|
||||
return nc->caps->ops->setup_data_interface(nand, csline, conf);
|
||||
return nc->caps->ops->setup_interface(nand, csline, conf);
|
||||
}
|
||||
|
||||
static void atmel_nand_init(struct atmel_nand_controller *nc,
|
||||
@ -1483,7 +1483,7 @@ static void atmel_nand_init(struct atmel_nand_controller *nc,
|
||||
chip->legacy.write_buf = atmel_nand_write_buf;
|
||||
chip->legacy.select_chip = atmel_nand_select_chip;
|
||||
|
||||
if (!nc->mck || !nc->caps->ops->setup_data_interface)
|
||||
if (!nc->mck || !nc->caps->ops->setup_interface)
|
||||
chip->options |= NAND_KEEP_TIMINGS;
|
||||
|
||||
/* Some NANDs require a longer delay than the default one (20us). */
|
||||
@ -1956,7 +1956,7 @@ static int atmel_nand_attach_chip(struct nand_chip *chip)
|
||||
|
||||
static const struct nand_controller_ops atmel_nand_controller_ops = {
|
||||
.attach_chip = atmel_nand_attach_chip,
|
||||
.setup_data_interface = atmel_nand_setup_data_interface,
|
||||
.setup_interface = atmel_nand_setup_interface,
|
||||
};
|
||||
|
||||
static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
|
||||
@ -2318,7 +2318,7 @@ static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
|
||||
.remove = atmel_hsmc_nand_controller_remove,
|
||||
.ecc_init = atmel_hsmc_nand_ecc_init,
|
||||
.nand_init = atmel_hsmc_nand_init,
|
||||
.setup_data_interface = atmel_hsmc_nand_setup_data_interface,
|
||||
.setup_interface = atmel_hsmc_nand_setup_interface,
|
||||
};
|
||||
|
||||
static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
|
||||
@ -2375,10 +2375,10 @@ atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
|
||||
|
||||
/*
|
||||
* The SMC reg layout of at91rm9200 is completely different which prevents us
|
||||
* from re-using atmel_smc_nand_setup_data_interface() for the
|
||||
* ->setup_data_interface() hook.
|
||||
* from re-using atmel_smc_nand_setup_interface() for the
|
||||
* ->setup_interface() hook.
|
||||
* At this point, there's no support for the at91rm9200 SMC IP, so we leave
|
||||
* ->setup_data_interface() unassigned.
|
||||
* ->setup_interface() unassigned.
|
||||
*/
|
||||
static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
|
||||
.probe = atmel_smc_nand_controller_probe,
|
||||
@ -2399,7 +2399,7 @@ static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
|
||||
.remove = atmel_smc_nand_controller_remove,
|
||||
.ecc_init = atmel_nand_ecc_init,
|
||||
.nand_init = atmel_smc_nand_init,
|
||||
.setup_data_interface = atmel_smc_nand_setup_data_interface,
|
||||
.setup_interface = atmel_smc_nand_setup_interface,
|
||||
};
|
||||
|
||||
static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
|
||||
|
@ -1918,6 +1918,22 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
|
||||
edu_writel(ctrl, EDU_STOP, 0); /* force stop */
|
||||
edu_readl(ctrl, EDU_STOP);
|
||||
|
||||
if (!ret && edu_cmd == EDU_CMD_READ) {
|
||||
u64 err_addr = 0;
|
||||
|
||||
/*
|
||||
* check for ECC errors here, subpage ECC errors are
|
||||
* retained in ECC error address register
|
||||
*/
|
||||
err_addr = brcmnand_get_uncorrecc_addr(ctrl);
|
||||
if (!err_addr) {
|
||||
err_addr = brcmnand_get_correcc_addr(ctrl);
|
||||
if (err_addr)
|
||||
ret = -EUCLEAN;
|
||||
} else
|
||||
ret = -EBADMSG;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2124,6 +2140,7 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
u64 err_addr = 0;
|
||||
int err;
|
||||
bool retry = true;
|
||||
bool edu_err = false;
|
||||
|
||||
dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
|
||||
|
||||
@ -2141,6 +2158,10 @@ try_dmaread:
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (has_edu(ctrl) && err_addr)
|
||||
edu_err = true;
|
||||
|
||||
} else {
|
||||
if (oob)
|
||||
memset(oob, 0x99, mtd->oobsize);
|
||||
@ -2188,6 +2209,11 @@ try_dmaread:
|
||||
if (mtd_is_bitflip(err)) {
|
||||
unsigned int corrected = brcmnand_count_corrected(ctrl);
|
||||
|
||||
/* in case of EDU correctable error we read again using PIO */
|
||||
if (edu_err)
|
||||
err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
|
||||
oob, &err_addr);
|
||||
|
||||
dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
|
||||
(unsigned long long)err_addr);
|
||||
mtd->ecc_stats.corrected += corrected;
|
||||
@ -3023,8 +3049,9 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* set edu transfer function to call */
|
||||
ctrl->dma_trans = brcmnand_edu_trans;
|
||||
if (has_edu(ctrl))
|
||||
/* set edu transfer function to call */
|
||||
ctrl->dma_trans = brcmnand_edu_trans;
|
||||
}
|
||||
|
||||
/* Disable automatic device ID config, direct addressing */
|
||||
|
@ -2304,8 +2304,8 @@ static inline u32 calc_tdvw(u32 trp_cnt, u32 clk_period, u32 trhoh_min,
|
||||
}
|
||||
|
||||
static int
|
||||
cadence_nand_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr;
|
||||
struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller);
|
||||
@ -2691,7 +2691,7 @@ static int cadence_nand_attach_chip(struct nand_chip *chip)
|
||||
static const struct nand_controller_ops cadence_nand_controller_ops = {
|
||||
.attach_chip = cadence_nand_attach_chip,
|
||||
.exec_op = cadence_nand_exec_op,
|
||||
.setup_data_interface = cadence_nand_setup_data_interface,
|
||||
.setup_interface = cadence_nand_setup_interface,
|
||||
};
|
||||
|
||||
static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,
|
||||
|
@ -761,8 +761,8 @@ static int denali_write_page(struct nand_chip *chip, const u8 *buf,
|
||||
return denali_page_xfer(chip, (void *)buf, mtd->writesize, page, true);
|
||||
}
|
||||
|
||||
static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
static int denali_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
static const unsigned int data_setup_on_host = 10000;
|
||||
struct denali_controller *denali = to_denali_controller(chip);
|
||||
@ -1173,7 +1173,7 @@ static int denali_exec_op(struct nand_chip *chip,
|
||||
static const struct nand_controller_ops denali_controller_ops = {
|
||||
.attach_chip = denali_attach_chip,
|
||||
.exec_op = denali_exec_op,
|
||||
.setup_data_interface = denali_setup_data_interface,
|
||||
.setup_interface = denali_setup_interface,
|
||||
};
|
||||
|
||||
int denali_chip_init(struct denali_controller *denali,
|
||||
@ -1230,7 +1230,7 @@ int denali_chip_init(struct denali_controller *denali,
|
||||
chip->buf_align = 16;
|
||||
}
|
||||
|
||||
/* clk rate info is needed for setup_data_interface */
|
||||
/* clk rate info is needed for setup_interface */
|
||||
if (!denali->clk_rate || !denali->clk_x_rate)
|
||||
chip->options |= NAND_KEEP_TIMINGS;
|
||||
|
||||
|
@ -14,32 +14,23 @@
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/fsl_lbc.h>
|
||||
|
||||
#define FSL_UPM_WAIT_RUN_PATTERN 0x1
|
||||
#define FSL_UPM_WAIT_WRITE_BYTE 0x2
|
||||
#define FSL_UPM_WAIT_WRITE_BUFFER 0x4
|
||||
|
||||
struct fsl_upm_nand {
|
||||
struct nand_controller base;
|
||||
struct device *dev;
|
||||
struct nand_chip chip;
|
||||
int last_ctrl;
|
||||
struct mtd_partition *parts;
|
||||
struct fsl_upm upm;
|
||||
uint8_t upm_addr_offset;
|
||||
uint8_t upm_cmd_offset;
|
||||
void __iomem *io_base;
|
||||
int rnb_gpio[NAND_MAX_CHIPS];
|
||||
struct gpio_desc *rnb_gpio[NAND_MAX_CHIPS];
|
||||
uint32_t mchip_offsets[NAND_MAX_CHIPS];
|
||||
uint32_t mchip_count;
|
||||
uint32_t mchip_number;
|
||||
int chip_delay;
|
||||
uint32_t wait_flags;
|
||||
};
|
||||
|
||||
static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo)
|
||||
@ -48,106 +39,6 @@ static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo)
|
||||
chip);
|
||||
}
|
||||
|
||||
static int fun_chip_ready(struct nand_chip *chip)
|
||||
{
|
||||
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
|
||||
|
||||
if (gpio_get_value(fun->rnb_gpio[fun->mchip_number]))
|
||||
return 1;
|
||||
|
||||
dev_vdbg(fun->dev, "busy\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fun_wait_rnb(struct fsl_upm_nand *fun)
|
||||
{
|
||||
if (fun->rnb_gpio[fun->mchip_number] >= 0) {
|
||||
struct mtd_info *mtd = nand_to_mtd(&fun->chip);
|
||||
int cnt = 1000000;
|
||||
|
||||
while (--cnt && !fun_chip_ready(&fun->chip))
|
||||
cpu_relax();
|
||||
if (!cnt)
|
||||
dev_err(fun->dev, "tired waiting for RNB\n");
|
||||
} else {
|
||||
ndelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
static void fun_cmd_ctrl(struct nand_chip *chip, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
|
||||
u32 mar;
|
||||
|
||||
if (!(ctrl & fun->last_ctrl)) {
|
||||
fsl_upm_end_pattern(&fun->upm);
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE);
|
||||
}
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
if (ctrl & NAND_ALE)
|
||||
fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
|
||||
else if (ctrl & NAND_CLE)
|
||||
fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
|
||||
}
|
||||
|
||||
mar = (cmd << (32 - fun->upm.width)) |
|
||||
fun->mchip_offsets[fun->mchip_number];
|
||||
fsl_upm_run_pattern(&fun->upm, chip->legacy.IO_ADDR_R, mar);
|
||||
|
||||
if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN)
|
||||
fun_wait_rnb(fun);
|
||||
}
|
||||
|
||||
static void fun_select_chip(struct nand_chip *chip, int mchip_nr)
|
||||
{
|
||||
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
|
||||
|
||||
if (mchip_nr == -1) {
|
||||
chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
|
||||
} else if (mchip_nr >= 0 && mchip_nr < NAND_MAX_CHIPS) {
|
||||
fun->mchip_number = mchip_nr;
|
||||
chip->legacy.IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr];
|
||||
chip->legacy.IO_ADDR_W = chip->legacy.IO_ADDR_R;
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t fun_read_byte(struct nand_chip *chip)
|
||||
{
|
||||
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
|
||||
|
||||
return in_8(fun->chip.legacy.IO_ADDR_R);
|
||||
}
|
||||
|
||||
static void fun_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
|
||||
{
|
||||
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
buf[i] = in_8(fun->chip.legacy.IO_ADDR_R);
|
||||
}
|
||||
|
||||
static void fun_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
|
||||
{
|
||||
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
out_8(fun->chip.legacy.IO_ADDR_W, buf[i]);
|
||||
if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE)
|
||||
fun_wait_rnb(fun);
|
||||
}
|
||||
if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER)
|
||||
fun_wait_rnb(fun);
|
||||
}
|
||||
|
||||
static int fun_chip_init(struct fsl_upm_nand *fun,
|
||||
const struct device_node *upm_np,
|
||||
const struct resource *io_res)
|
||||
@ -156,21 +47,9 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
|
||||
int ret;
|
||||
struct device_node *flash_np;
|
||||
|
||||
fun->chip.legacy.IO_ADDR_R = fun->io_base;
|
||||
fun->chip.legacy.IO_ADDR_W = fun->io_base;
|
||||
fun->chip.legacy.cmd_ctrl = fun_cmd_ctrl;
|
||||
fun->chip.legacy.chip_delay = fun->chip_delay;
|
||||
fun->chip.legacy.read_byte = fun_read_byte;
|
||||
fun->chip.legacy.read_buf = fun_read_buf;
|
||||
fun->chip.legacy.write_buf = fun_write_buf;
|
||||
fun->chip.ecc.mode = NAND_ECC_SOFT;
|
||||
fun->chip.ecc.algo = NAND_ECC_HAMMING;
|
||||
if (fun->mchip_count > 1)
|
||||
fun->chip.legacy.select_chip = fun_select_chip;
|
||||
|
||||
if (fun->rnb_gpio[0] >= 0)
|
||||
fun->chip.legacy.dev_ready = fun_chip_ready;
|
||||
|
||||
fun->chip.controller = &fun->base;
|
||||
mtd->dev.parent = fun->dev;
|
||||
|
||||
flash_np = of_get_next_child(upm_np, NULL);
|
||||
@ -178,8 +57,9 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
|
||||
return -ENODEV;
|
||||
|
||||
nand_set_flash_node(&fun->chip, flash_np);
|
||||
mtd->name = kasprintf(GFP_KERNEL, "0x%llx.%pOFn", (u64)io_res->start,
|
||||
flash_np);
|
||||
mtd->name = devm_kasprintf(fun->dev, GFP_KERNEL, "0x%llx.%pOFn",
|
||||
(u64)io_res->start,
|
||||
flash_np);
|
||||
if (!mtd->name) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
@ -192,51 +72,130 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
err:
|
||||
of_node_put(flash_np);
|
||||
if (ret)
|
||||
kfree(mtd->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int func_exec_instr(struct nand_chip *chip,
|
||||
const struct nand_op_instr *instr)
|
||||
{
|
||||
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
|
||||
u32 mar, reg_offs = fun->mchip_offsets[fun->mchip_number];
|
||||
unsigned int i;
|
||||
const u8 *out;
|
||||
u8 *in;
|
||||
|
||||
switch (instr->type) {
|
||||
case NAND_OP_CMD_INSTR:
|
||||
fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
|
||||
mar = (instr->ctx.cmd.opcode << (32 - fun->upm.width)) |
|
||||
reg_offs;
|
||||
fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar);
|
||||
fsl_upm_end_pattern(&fun->upm);
|
||||
return 0;
|
||||
|
||||
case NAND_OP_ADDR_INSTR:
|
||||
fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
|
||||
for (i = 0; i < instr->ctx.addr.naddrs; i++) {
|
||||
mar = (instr->ctx.addr.addrs[i] << (32 - fun->upm.width)) |
|
||||
reg_offs;
|
||||
fsl_upm_run_pattern(&fun->upm, fun->io_base + reg_offs, mar);
|
||||
}
|
||||
fsl_upm_end_pattern(&fun->upm);
|
||||
return 0;
|
||||
|
||||
case NAND_OP_DATA_IN_INSTR:
|
||||
in = instr->ctx.data.buf.in;
|
||||
for (i = 0; i < instr->ctx.data.len; i++)
|
||||
in[i] = in_8(fun->io_base + reg_offs);
|
||||
return 0;
|
||||
|
||||
case NAND_OP_DATA_OUT_INSTR:
|
||||
out = instr->ctx.data.buf.out;
|
||||
for (i = 0; i < instr->ctx.data.len; i++)
|
||||
out_8(fun->io_base + reg_offs, out[i]);
|
||||
return 0;
|
||||
|
||||
case NAND_OP_WAITRDY_INSTR:
|
||||
if (!fun->rnb_gpio[fun->mchip_number])
|
||||
return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms);
|
||||
|
||||
return nand_gpio_waitrdy(chip, fun->rnb_gpio[fun->mchip_number],
|
||||
instr->ctx.waitrdy.timeout_ms);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fun_exec_op(struct nand_chip *chip, const struct nand_operation *op,
|
||||
bool check_only)
|
||||
{
|
||||
struct fsl_upm_nand *fun = to_fsl_upm_nand(nand_to_mtd(chip));
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (op->cs > NAND_MAX_CHIPS)
|
||||
return -EINVAL;
|
||||
|
||||
if (check_only)
|
||||
return 0;
|
||||
|
||||
fun->mchip_number = op->cs;
|
||||
|
||||
for (i = 0; i < op->ninstrs; i++) {
|
||||
ret = func_exec_instr(chip, &op->instrs[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (op->instrs[i].delay_ns)
|
||||
ndelay(op->instrs[i].delay_ns);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops fun_ops = {
|
||||
.exec_op = fun_exec_op,
|
||||
};
|
||||
|
||||
static int fun_probe(struct platform_device *ofdev)
|
||||
{
|
||||
struct fsl_upm_nand *fun;
|
||||
struct resource io_res;
|
||||
struct resource *io_res;
|
||||
const __be32 *prop;
|
||||
int rnb_gpio;
|
||||
int ret;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
fun = kzalloc(sizeof(*fun), GFP_KERNEL);
|
||||
fun = devm_kzalloc(&ofdev->dev, sizeof(*fun), GFP_KERNEL);
|
||||
if (!fun)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_address_to_resource(ofdev->dev.of_node, 0, &io_res);
|
||||
if (ret) {
|
||||
dev_err(&ofdev->dev, "can't get IO base\n");
|
||||
goto err1;
|
||||
}
|
||||
io_res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
|
||||
fun->io_base = devm_ioremap_resource(&ofdev->dev, io_res);
|
||||
if (IS_ERR(fun->io_base))
|
||||
return PTR_ERR(fun->io_base);
|
||||
|
||||
ret = fsl_upm_find(io_res.start, &fun->upm);
|
||||
ret = fsl_upm_find(io_res->start, &fun->upm);
|
||||
if (ret) {
|
||||
dev_err(&ofdev->dev, "can't find UPM\n");
|
||||
goto err1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-addr-offset",
|
||||
&size);
|
||||
if (!prop || size != sizeof(uint32_t)) {
|
||||
dev_err(&ofdev->dev, "can't get UPM address offset\n");
|
||||
ret = -EINVAL;
|
||||
goto err1;
|
||||
return -EINVAL;
|
||||
}
|
||||
fun->upm_addr_offset = *prop;
|
||||
|
||||
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-cmd-offset", &size);
|
||||
if (!prop || size != sizeof(uint32_t)) {
|
||||
dev_err(&ofdev->dev, "can't get UPM command offset\n");
|
||||
ret = -EINVAL;
|
||||
goto err1;
|
||||
return -EINVAL;
|
||||
}
|
||||
fun->upm_cmd_offset = *prop;
|
||||
|
||||
@ -246,7 +205,7 @@ static int fun_probe(struct platform_device *ofdev)
|
||||
fun->mchip_count = size / sizeof(uint32_t);
|
||||
if (fun->mchip_count >= NAND_MAX_CHIPS) {
|
||||
dev_err(&ofdev->dev, "too much multiple chips\n");
|
||||
goto err1;
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < fun->mchip_count; i++)
|
||||
fun->mchip_offsets[i] = be32_to_cpu(prop[i]);
|
||||
@ -255,63 +214,26 @@ static int fun_probe(struct platform_device *ofdev)
|
||||
}
|
||||
|
||||
for (i = 0; i < fun->mchip_count; i++) {
|
||||
fun->rnb_gpio[i] = -1;
|
||||
rnb_gpio = of_get_gpio(ofdev->dev.of_node, i);
|
||||
if (rnb_gpio >= 0) {
|
||||
ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev));
|
||||
if (ret) {
|
||||
dev_err(&ofdev->dev,
|
||||
"can't request RNB gpio #%d\n", i);
|
||||
goto err2;
|
||||
}
|
||||
gpio_direction_input(rnb_gpio);
|
||||
fun->rnb_gpio[i] = rnb_gpio;
|
||||
} else if (rnb_gpio == -EINVAL) {
|
||||
fun->rnb_gpio[i] = devm_gpiod_get_index_optional(&ofdev->dev,
|
||||
NULL, i,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(fun->rnb_gpio[i])) {
|
||||
dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i);
|
||||
goto err2;
|
||||
return PTR_ERR(fun->rnb_gpio[i]);
|
||||
}
|
||||
}
|
||||
|
||||
prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL);
|
||||
if (prop)
|
||||
fun->chip_delay = be32_to_cpup(prop);
|
||||
else
|
||||
fun->chip_delay = 50;
|
||||
|
||||
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size);
|
||||
if (prop && size == sizeof(uint32_t))
|
||||
fun->wait_flags = be32_to_cpup(prop);
|
||||
else
|
||||
fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
|
||||
FSL_UPM_WAIT_WRITE_BYTE;
|
||||
|
||||
fun->io_base = devm_ioremap(&ofdev->dev, io_res.start,
|
||||
resource_size(&io_res));
|
||||
if (!fun->io_base) {
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
nand_controller_init(&fun->base);
|
||||
fun->base.ops = &fun_ops;
|
||||
fun->dev = &ofdev->dev;
|
||||
fun->last_ctrl = NAND_CLE;
|
||||
|
||||
ret = fun_chip_init(fun, ofdev->dev.of_node, &io_res);
|
||||
ret = fun_chip_init(fun, ofdev->dev.of_node, io_res);
|
||||
if (ret)
|
||||
goto err2;
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, fun);
|
||||
|
||||
return 0;
|
||||
err2:
|
||||
for (i = 0; i < fun->mchip_count; i++) {
|
||||
if (fun->rnb_gpio[i] < 0)
|
||||
break;
|
||||
gpio_free(fun->rnb_gpio[i]);
|
||||
}
|
||||
err1:
|
||||
kfree(fun);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fun_remove(struct platform_device *ofdev)
|
||||
@ -319,20 +241,11 @@ static int fun_remove(struct platform_device *ofdev)
|
||||
struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
|
||||
struct nand_chip *chip = &fun->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
ret = mtd_device_unregister(mtd);
|
||||
WARN_ON(ret);
|
||||
nand_cleanup(chip);
|
||||
kfree(mtd->name);
|
||||
|
||||
for (i = 0; i < fun->mchip_count; i++) {
|
||||
if (fun->rnb_gpio[i] < 0)
|
||||
break;
|
||||
gpio_free(fun->rnb_gpio[i]);
|
||||
}
|
||||
|
||||
kfree(fun);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -327,8 +327,8 @@ static int fsmc_calc_timings(struct fsmc_nand_data *host,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsmc_setup_data_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int fsmc_setup_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct fsmc_nand_data *host = nand_to_fsmc(nand);
|
||||
struct fsmc_nand_timings tims;
|
||||
@ -951,7 +951,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand)
|
||||
static const struct nand_controller_ops fsmc_nand_controller_ops = {
|
||||
.attach_chip = fsmc_nand_attach_chip,
|
||||
.exec_op = fsmc_exec_op,
|
||||
.setup_data_interface = fsmc_setup_data_interface,
|
||||
.setup_interface = fsmc_setup_interface,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -25,8 +25,11 @@
|
||||
#include <linux/mtd/nand-gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
struct gpiomtd {
|
||||
struct nand_controller base;
|
||||
void __iomem *io;
|
||||
void __iomem *io_sync;
|
||||
struct nand_chip nand_chip;
|
||||
struct gpio_nand_platdata plat;
|
||||
@ -69,34 +72,99 @@ static void gpio_nand_dosync(struct gpiomtd *gpiomtd)
|
||||
static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {}
|
||||
#endif
|
||||
|
||||
static void gpio_nand_cmd_ctrl(struct nand_chip *chip, int cmd,
|
||||
unsigned int ctrl)
|
||||
static int gpio_nand_exec_instr(struct nand_chip *chip,
|
||||
const struct nand_op_instr *instr)
|
||||
{
|
||||
struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip));
|
||||
unsigned int i;
|
||||
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
if (gpiomtd->nce)
|
||||
gpiod_set_value(gpiomtd->nce, !(ctrl & NAND_NCE));
|
||||
gpiod_set_value(gpiomtd->cle, !!(ctrl & NAND_CLE));
|
||||
gpiod_set_value(gpiomtd->ale, !!(ctrl & NAND_ALE));
|
||||
switch (instr->type) {
|
||||
case NAND_OP_CMD_INSTR:
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
}
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
gpiod_set_value(gpiomtd->cle, 1);
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
writeb(instr->ctx.cmd.opcode, gpiomtd->io);
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
gpiod_set_value(gpiomtd->cle, 0);
|
||||
return 0;
|
||||
|
||||
writeb(cmd, gpiomtd->nand_chip.legacy.IO_ADDR_W);
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
case NAND_OP_ADDR_INSTR:
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
gpiod_set_value(gpiomtd->ale, 1);
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
for (i = 0; i < instr->ctx.addr.naddrs; i++)
|
||||
writeb(instr->ctx.addr.addrs[i], gpiomtd->io);
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
gpiod_set_value(gpiomtd->ale, 0);
|
||||
return 0;
|
||||
|
||||
case NAND_OP_DATA_IN_INSTR:
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
if ((chip->options & NAND_BUSWIDTH_16) &&
|
||||
!instr->ctx.data.force_8bit)
|
||||
ioread16_rep(gpiomtd->io, instr->ctx.data.buf.in,
|
||||
instr->ctx.data.len / 2);
|
||||
else
|
||||
ioread8_rep(gpiomtd->io, instr->ctx.data.buf.in,
|
||||
instr->ctx.data.len);
|
||||
return 0;
|
||||
|
||||
case NAND_OP_DATA_OUT_INSTR:
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
if ((chip->options & NAND_BUSWIDTH_16) &&
|
||||
!instr->ctx.data.force_8bit)
|
||||
iowrite16_rep(gpiomtd->io, instr->ctx.data.buf.out,
|
||||
instr->ctx.data.len / 2);
|
||||
else
|
||||
iowrite8_rep(gpiomtd->io, instr->ctx.data.buf.out,
|
||||
instr->ctx.data.len);
|
||||
return 0;
|
||||
|
||||
case NAND_OP_WAITRDY_INSTR:
|
||||
if (!gpiomtd->rdy)
|
||||
return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms);
|
||||
|
||||
return nand_gpio_waitrdy(chip, gpiomtd->rdy,
|
||||
instr->ctx.waitrdy.timeout_ms);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_nand_devready(struct nand_chip *chip)
|
||||
static int gpio_nand_exec_op(struct nand_chip *chip,
|
||||
const struct nand_operation *op,
|
||||
bool check_only)
|
||||
{
|
||||
struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip));
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
|
||||
return gpiod_get_value(gpiomtd->rdy);
|
||||
if (check_only)
|
||||
return 0;
|
||||
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
gpiod_set_value(gpiomtd->nce, 0);
|
||||
for (i = 0; i < op->ninstrs; i++) {
|
||||
ret = gpio_nand_exec_instr(chip, &op->instrs[i]);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (op->instrs[i].delay_ns)
|
||||
ndelay(op->instrs[i].delay_ns);
|
||||
}
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
gpiod_set_value(gpiomtd->nce, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops gpio_nand_ops = {
|
||||
.exec_op = gpio_nand_exec_op,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id gpio_nand_id_table[] = {
|
||||
{ .compatible = "gpio-control-nand" },
|
||||
@ -225,9 +293,9 @@ static int gpio_nand_probe(struct platform_device *pdev)
|
||||
chip = &gpiomtd->nand_chip;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
chip->legacy.IO_ADDR_R = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(chip->legacy.IO_ADDR_R))
|
||||
return PTR_ERR(chip->legacy.IO_ADDR_R);
|
||||
gpiomtd->io = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(gpiomtd->io))
|
||||
return PTR_ERR(gpiomtd->io);
|
||||
|
||||
res = gpio_nand_get_io_sync(pdev);
|
||||
if (res) {
|
||||
@ -269,17 +337,15 @@ static int gpio_nand_probe(struct platform_device *pdev)
|
||||
ret = PTR_ERR(gpiomtd->rdy);
|
||||
goto out_ce;
|
||||
}
|
||||
/* Using RDY pin */
|
||||
if (gpiomtd->rdy)
|
||||
chip->legacy.dev_ready = gpio_nand_devready;
|
||||
|
||||
nand_controller_init(&gpiomtd->base);
|
||||
gpiomtd->base.ops = &gpio_nand_ops;
|
||||
|
||||
nand_set_flash_node(chip, pdev->dev.of_node);
|
||||
chip->legacy.IO_ADDR_W = chip->legacy.IO_ADDR_R;
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->ecc.algo = NAND_ECC_HAMMING;
|
||||
chip->options = gpiomtd->plat.options;
|
||||
chip->legacy.chip_delay = gpiomtd->plat.chip_delay;
|
||||
chip->legacy.cmd_ctrl = gpio_nand_cmd_ctrl;
|
||||
chip->controller = &gpiomtd->base;
|
||||
|
||||
mtd = nand_to_mtd(chip);
|
||||
mtd->dev.parent = dev;
|
||||
|
@ -736,8 +736,8 @@ static void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
|
||||
udelay(dll_wait_time_us);
|
||||
}
|
||||
|
||||
static int gpmi_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
static int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct gpmi_nand_data *this = nand_get_controller_data(chip);
|
||||
const struct nand_sdr_timings *sdr;
|
||||
@ -2400,7 +2400,7 @@ unmap:
|
||||
|
||||
static const struct nand_controller_ops gpmi_nand_controller_ops = {
|
||||
.attach_chip = gpmi_nand_attach_chip,
|
||||
.setup_data_interface = gpmi_setup_data_interface,
|
||||
.setup_interface = gpmi_setup_interface,
|
||||
.exec_op = gpmi_nfc_exec_op,
|
||||
};
|
||||
|
||||
|
@ -90,8 +90,8 @@ static int jz4740_ecc_calculate(struct ingenic_ecc *ecc,
|
||||
* If the written data is completely 0xff, we also want to write 0xff as
|
||||
* ECC, otherwise we will get in trouble when doing subpage writes.
|
||||
*/
|
||||
if (memcmp(ecc_code, empty_block_ecc, ARRAY_SIZE(empty_block_ecc)) == 0)
|
||||
memset(ecc_code, 0xff, ARRAY_SIZE(empty_block_ecc));
|
||||
if (memcmp(ecc_code, empty_block_ecc, sizeof(empty_block_ecc)) == 0)
|
||||
memset(ecc_code, 0xff, sizeof(empty_block_ecc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -53,12 +53,12 @@ struct nand_manufacturer_ops {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_manufacturer - NAND Flash Manufacturer structure
|
||||
* struct nand_manufacturer_desc - NAND Flash Manufacturer descriptor
|
||||
* @name: Manufacturer name
|
||||
* @id: manufacturer ID code of device.
|
||||
* @ops: manufacturer operations
|
||||
*/
|
||||
struct nand_manufacturer {
|
||||
struct nand_manufacturer_desc {
|
||||
int id;
|
||||
char *name;
|
||||
const struct nand_manufacturer_ops *ops;
|
||||
@ -79,14 +79,21 @@ extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops;
|
||||
extern const struct mtd_pairing_scheme dist3_pairing_scheme;
|
||||
|
||||
/* Core functions */
|
||||
const struct nand_manufacturer *nand_get_manufacturer(u8 id);
|
||||
const struct nand_manufacturer_desc *nand_get_manufacturer_desc(u8 id);
|
||||
int nand_bbm_get_next_page(struct nand_chip *chip, int page);
|
||||
int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs);
|
||||
int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
|
||||
int allowbbt);
|
||||
int onfi_fill_data_interface(struct nand_chip *chip,
|
||||
enum nand_data_interface_type type,
|
||||
int 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);
|
||||
unsigned int
|
||||
onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings);
|
||||
int nand_choose_best_sdr_timings(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
struct nand_sdr_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);
|
||||
int nand_read_page_raw_notsupp(struct nand_chip *chip, u8 *buf,
|
||||
@ -130,10 +137,10 @@ static inline int nand_exec_op(struct nand_chip *chip,
|
||||
return chip->controller->ops->exec_op(chip, op, false);
|
||||
}
|
||||
|
||||
static inline bool nand_has_setup_data_iface(struct nand_chip *chip)
|
||||
static inline bool nand_controller_can_setup_interface(struct nand_chip *chip)
|
||||
{
|
||||
if (!chip->controller || !chip->controller->ops ||
|
||||
!chip->controller->ops->setup_data_interface)
|
||||
!chip->controller->ops->setup_interface)
|
||||
return false;
|
||||
|
||||
if (chip->options & NAND_KEEP_TIMINGS)
|
||||
|
@ -1096,6 +1096,8 @@ static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip,
|
||||
const u8 *oob_buf, bool raw,
|
||||
int page)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
|
||||
struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
|
||||
const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
|
||||
@ -1141,7 +1143,7 @@ static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip,
|
||||
return ret;
|
||||
|
||||
ret = marvell_nfc_wait_op(chip,
|
||||
PSEC_TO_MSEC(chip->data_interface.timings.sdr.tPROG_max));
|
||||
PSEC_TO_MSEC(sdr->tPROG_max));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1562,6 +1564,8 @@ static int marvell_nfc_hw_ecc_bch_write_page(struct nand_chip *chip,
|
||||
const u8 *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
|
||||
const u8 *data = buf;
|
||||
@ -1598,8 +1602,7 @@ static int marvell_nfc_hw_ecc_bch_write_page(struct nand_chip *chip,
|
||||
marvell_nfc_wait_ndrun(chip);
|
||||
}
|
||||
|
||||
ret = marvell_nfc_wait_op(chip,
|
||||
PSEC_TO_MSEC(chip->data_interface.timings.sdr.tPROG_max));
|
||||
ret = marvell_nfc_wait_op(chip, PSEC_TO_MSEC(sdr->tPROG_max));
|
||||
|
||||
marvell_nfc_disable_hw_ecc(chip);
|
||||
|
||||
@ -2305,9 +2308,8 @@ static struct nand_bbt_descr bbt_mirror_descr = {
|
||||
.pattern = bbt_mirror_pattern
|
||||
};
|
||||
|
||||
static int marvell_nfc_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface
|
||||
*conf)
|
||||
static int marvell_nfc_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
|
||||
struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
|
||||
@ -2508,7 +2510,7 @@ static int marvell_nand_attach_chip(struct nand_chip *chip)
|
||||
static const struct nand_controller_ops marvell_nand_controller_ops = {
|
||||
.attach_chip = marvell_nand_attach_chip,
|
||||
.exec_op = marvell_nfc_exec_op,
|
||||
.setup_data_interface = marvell_nfc_setup_data_interface,
|
||||
.setup_interface = marvell_nfc_setup_interface,
|
||||
};
|
||||
|
||||
static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
|
||||
@ -2644,7 +2646,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
|
||||
|
||||
/*
|
||||
* Save a reference value for timing registers before
|
||||
* ->setup_data_interface() is called.
|
||||
* ->setup_interface() is called.
|
||||
*/
|
||||
marvell_nand->ndtr0 = readl_relaxed(nfc->regs + NDTR0);
|
||||
marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1);
|
||||
|
@ -573,10 +573,10 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
|
||||
static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
|
||||
int page, bool in)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(nand));
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
struct meson_nfc *nfc = nand_get_controller_data(nand);
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&nand->data_interface);
|
||||
u32 *addrs = nfc->cmdfifo.rw.addrs;
|
||||
u32 cs = nfc->param.chip_select;
|
||||
u32 cmd0, cmd_num, row_start;
|
||||
@ -626,9 +626,9 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
|
||||
static int meson_nfc_write_page_sub(struct nand_chip *nand,
|
||||
int page, int raw)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&nand->data_interface);
|
||||
nand_get_sdr_timings(nand_get_interface_config(nand));
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
|
||||
struct meson_nfc *nfc = nand_get_controller_data(nand);
|
||||
int data_len, info_len;
|
||||
@ -1097,8 +1097,8 @@ static int meson_chip_buffer_init(struct nand_chip *nand)
|
||||
}
|
||||
|
||||
static
|
||||
int meson_nfc_setup_data_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
int meson_nfc_setup_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
|
||||
const struct nand_sdr_timings *timings;
|
||||
@ -1222,7 +1222,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
|
||||
static const struct nand_controller_ops meson_nand_controller_ops = {
|
||||
.attach_chip = meson_nand_attach_chip,
|
||||
.detach_chip = meson_nand_detach_chip,
|
||||
.setup_data_interface = meson_nfc_setup_data_interface,
|
||||
.setup_interface = meson_nfc_setup_interface,
|
||||
.exec_op = meson_nfc_exec_op,
|
||||
};
|
||||
|
||||
|
@ -387,44 +387,6 @@ static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_nfc_select_chip(struct nand_chip *nand, int chip)
|
||||
{
|
||||
struct mtk_nfc *nfc = nand_get_controller_data(nand);
|
||||
struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand);
|
||||
|
||||
if (chip < 0)
|
||||
return;
|
||||
|
||||
mtk_nfc_hw_runtime_config(nand_to_mtd(nand));
|
||||
|
||||
nfi_writel(nfc, mtk_nand->sels[chip], NFI_CSEL);
|
||||
}
|
||||
|
||||
static int mtk_nfc_dev_ready(struct nand_chip *nand)
|
||||
{
|
||||
struct mtk_nfc *nfc = nand_get_controller_data(nand);
|
||||
|
||||
if (nfi_readl(nfc, NFI_STA) & STA_BUSY)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void mtk_nfc_cmd_ctrl(struct nand_chip *chip, int dat,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct mtk_nfc *nfc = nand_get_controller_data(chip);
|
||||
|
||||
if (ctrl & NAND_ALE) {
|
||||
mtk_nfc_send_address(nfc, dat);
|
||||
} else if (ctrl & NAND_CLE) {
|
||||
mtk_nfc_hw_reset(nfc);
|
||||
|
||||
nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG);
|
||||
mtk_nfc_send_command(nfc, dat);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc)
|
||||
{
|
||||
int rc;
|
||||
@ -501,8 +463,76 @@ static void mtk_nfc_write_buf(struct nand_chip *chip, const u8 *buf, int len)
|
||||
mtk_nfc_write_byte(chip, buf[i]);
|
||||
}
|
||||
|
||||
static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int mtk_nfc_exec_instr(struct nand_chip *chip,
|
||||
const struct nand_op_instr *instr)
|
||||
{
|
||||
struct mtk_nfc *nfc = nand_get_controller_data(chip);
|
||||
unsigned int i;
|
||||
u32 status;
|
||||
|
||||
switch (instr->type) {
|
||||
case NAND_OP_CMD_INSTR:
|
||||
mtk_nfc_send_command(nfc, instr->ctx.cmd.opcode);
|
||||
return 0;
|
||||
case NAND_OP_ADDR_INSTR:
|
||||
for (i = 0; i < instr->ctx.addr.naddrs; i++)
|
||||
mtk_nfc_send_address(nfc, instr->ctx.addr.addrs[i]);
|
||||
return 0;
|
||||
case NAND_OP_DATA_IN_INSTR:
|
||||
mtk_nfc_read_buf(chip, instr->ctx.data.buf.in,
|
||||
instr->ctx.data.len);
|
||||
return 0;
|
||||
case NAND_OP_DATA_OUT_INSTR:
|
||||
mtk_nfc_write_buf(chip, instr->ctx.data.buf.out,
|
||||
instr->ctx.data.len);
|
||||
return 0;
|
||||
case NAND_OP_WAITRDY_INSTR:
|
||||
return readl_poll_timeout(nfc->regs + NFI_STA, status,
|
||||
status & STA_BUSY, 20,
|
||||
instr->ctx.waitrdy.timeout_ms);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void mtk_nfc_select_target(struct nand_chip *nand, unsigned int cs)
|
||||
{
|
||||
struct mtk_nfc *nfc = nand_get_controller_data(nand);
|
||||
struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand);
|
||||
|
||||
mtk_nfc_hw_runtime_config(nand_to_mtd(nand));
|
||||
|
||||
nfi_writel(nfc, mtk_nand->sels[cs], NFI_CSEL);
|
||||
}
|
||||
|
||||
static int mtk_nfc_exec_op(struct nand_chip *chip,
|
||||
const struct nand_operation *op,
|
||||
bool check_only)
|
||||
{
|
||||
struct mtk_nfc *nfc = nand_get_controller_data(chip);
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
|
||||
if (check_only)
|
||||
return 0;
|
||||
|
||||
mtk_nfc_hw_reset(nfc);
|
||||
nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG);
|
||||
mtk_nfc_select_target(chip, op->cs);
|
||||
|
||||
for (i = 0; i < op->ninstrs; i++) {
|
||||
ret = mtk_nfc_exec_instr(chip, &op->instrs[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_nfc_setup_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct mtk_nfc *nfc = nand_get_controller_data(chip);
|
||||
const struct nand_sdr_timings *timings;
|
||||
@ -803,6 +833,7 @@ static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
mtk_nfc_select_target(chip, chip->cur_cs);
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
|
||||
if (!raw) {
|
||||
@ -920,6 +951,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
u8 *buf;
|
||||
int rc;
|
||||
|
||||
mtk_nfc_select_target(chip, chip->cur_cs);
|
||||
start = data_offs / chip->ecc.size;
|
||||
end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size);
|
||||
|
||||
@ -1325,7 +1357,8 @@ static int mtk_nfc_attach_chip(struct nand_chip *chip)
|
||||
|
||||
static const struct nand_controller_ops mtk_nfc_controller_ops = {
|
||||
.attach_chip = mtk_nfc_attach_chip,
|
||||
.setup_data_interface = mtk_nfc_setup_data_interface,
|
||||
.setup_interface = mtk_nfc_setup_interface,
|
||||
.exec_op = mtk_nfc_exec_op,
|
||||
};
|
||||
|
||||
static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
|
||||
@ -1381,13 +1414,6 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
|
||||
nand_set_controller_data(nand, nfc);
|
||||
|
||||
nand->options |= NAND_USES_DMA | NAND_SUBPAGE_READ;
|
||||
nand->legacy.dev_ready = mtk_nfc_dev_ready;
|
||||
nand->legacy.select_chip = mtk_nfc_select_chip;
|
||||
nand->legacy.write_byte = mtk_nfc_write_byte;
|
||||
nand->legacy.write_buf = mtk_nfc_write_buf;
|
||||
nand->legacy.read_byte = mtk_nfc_read_byte;
|
||||
nand->legacy.read_buf = mtk_nfc_read_buf;
|
||||
nand->legacy.cmd_ctrl = mtk_nfc_cmd_ctrl;
|
||||
|
||||
/* set default mode in case dt entry is missing */
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
|
@ -137,8 +137,8 @@ struct mxc_nand_devtype_data {
|
||||
u32 (*get_ecc_status)(struct mxc_nand_host *);
|
||||
const struct mtd_ooblayout_ops *ooblayout;
|
||||
void (*select_chip)(struct nand_chip *chip, int cs);
|
||||
int (*setup_data_interface)(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf);
|
||||
int (*setup_interface)(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf);
|
||||
void (*enable_hwecc)(struct nand_chip *chip, bool enable);
|
||||
|
||||
/*
|
||||
@ -1139,8 +1139,8 @@ static void preset_v1(struct mtd_info *mtd)
|
||||
writew(0x4, NFC_V1_V2_WRPROT);
|
||||
}
|
||||
|
||||
static int mxc_nand_v2_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int mxc_nand_v2_setup_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct mxc_nand_host *host = nand_get_controller_data(chip);
|
||||
int tRC_min_ns, tRC_ps, ret;
|
||||
@ -1432,7 +1432,7 @@ static int mxc_nand_get_features(struct nand_chip *chip, int addr,
|
||||
}
|
||||
|
||||
/*
|
||||
* The generic flash bbt decriptors overlap with our ecc
|
||||
* The generic flash bbt descriptors overlap with our ecc
|
||||
* hardware, so define some i.MX specific ones.
|
||||
*/
|
||||
static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
|
||||
@ -1521,7 +1521,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
|
||||
.get_ecc_status = get_ecc_status_v2,
|
||||
.ooblayout = &mxc_v2_ooblayout_ops,
|
||||
.select_chip = mxc_nand_select_chip_v2,
|
||||
.setup_data_interface = mxc_nand_v2_setup_data_interface,
|
||||
.setup_interface = mxc_nand_v2_setup_interface,
|
||||
.enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
|
||||
.irqpending_quirk = 0,
|
||||
.needs_ip = 0,
|
||||
@ -1738,17 +1738,17 @@ static int mxcnd_attach_chip(struct nand_chip *chip)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxcnd_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
static int mxcnd_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct mxc_nand_host *host = nand_get_controller_data(chip);
|
||||
|
||||
return host->devtype_data->setup_data_interface(chip, chipnr, conf);
|
||||
return host->devtype_data->setup_interface(chip, chipnr, conf);
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops mxcnd_controller_ops = {
|
||||
.attach_chip = mxcnd_attach_chip,
|
||||
.setup_data_interface = mxcnd_setup_data_interface,
|
||||
.setup_interface = mxcnd_setup_interface,
|
||||
};
|
||||
|
||||
static int mxcnd_probe(struct platform_device *pdev)
|
||||
@ -1809,7 +1809,7 @@ static int mxcnd_probe(struct platform_device *pdev)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!host->devtype_data->setup_data_interface)
|
||||
if (!host->devtype_data->setup_interface)
|
||||
this->options |= NAND_KEEP_TIMINGS;
|
||||
|
||||
if (host->devtype_data->needs_ip) {
|
||||
|
@ -451,8 +451,8 @@ static int mxic_nfc_exec_op(struct nand_chip *chip,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxic_nfc_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
static int mxic_nfc_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip);
|
||||
const struct nand_sdr_timings *sdr;
|
||||
@ -480,7 +480,7 @@ static int mxic_nfc_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
|
||||
static const struct nand_controller_ops mxic_nand_controller_ops = {
|
||||
.exec_op = mxic_nfc_exec_op,
|
||||
.setup_data_interface = mxic_nfc_setup_data_interface,
|
||||
.setup_interface = mxic_nfc_setup_interface,
|
||||
};
|
||||
|
||||
static int mxic_nfc_probe(struct platform_device *pdev)
|
||||
|
@ -773,7 +773,7 @@ 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(&chip->data_interface);
|
||||
timings = nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
ndelay(PSEC_TO_NSEC(timings->tWB_max));
|
||||
|
||||
ret = nand_status_op(chip, NULL);
|
||||
@ -898,7 +898,7 @@ static bool nand_supports_set_features(struct nand_chip *chip, int addr)
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_reset_data_interface - Reset data interface and timings
|
||||
* nand_reset_interface - Reset data interface and timings
|
||||
* @chip: The NAND chip
|
||||
* @chipnr: Internal die id
|
||||
*
|
||||
@ -906,11 +906,12 @@ static bool nand_supports_set_features(struct nand_chip *chip, int addr)
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise.
|
||||
*/
|
||||
static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
|
||||
static int nand_reset_interface(struct nand_chip *chip, int chipnr)
|
||||
{
|
||||
const struct nand_controller_ops *ops = chip->controller->ops;
|
||||
int ret;
|
||||
|
||||
if (!nand_has_setup_data_iface(chip))
|
||||
if (!nand_controller_can_setup_interface(chip))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -927,9 +928,9 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
|
||||
* timings to timing mode 0.
|
||||
*/
|
||||
|
||||
onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
|
||||
ret = chip->controller->ops->setup_data_interface(chip, chipnr,
|
||||
&chip->data_interface);
|
||||
chip->current_interface_config = nand_get_reset_interface_config();
|
||||
ret = ops->setup_interface(chip, chipnr,
|
||||
chip->current_interface_config);
|
||||
if (ret)
|
||||
pr_err("Failed to configure data interface to SDR timing mode 0\n");
|
||||
|
||||
@ -937,28 +938,36 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_setup_data_interface - Setup the best data interface and timings
|
||||
* nand_setup_interface - Setup the best data interface and timings
|
||||
* @chip: The NAND chip
|
||||
* @chipnr: Internal die id
|
||||
*
|
||||
* Find and configure the best data interface and NAND timings supported by
|
||||
* the chip and the driver.
|
||||
* First tries to retrieve supported timing modes from ONFI information,
|
||||
* and if the NAND chip does not support ONFI, relies on the
|
||||
* ->onfi_timing_mode_default specified in the nand_ids table.
|
||||
* Configure what has been reported to be the best data interface and NAND
|
||||
* timings supported by the chip and the driver.
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise.
|
||||
*/
|
||||
static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
|
||||
static int nand_setup_interface(struct nand_chip *chip, int chipnr)
|
||||
{
|
||||
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
|
||||
chip->onfi_timing_mode_default,
|
||||
};
|
||||
const struct nand_controller_ops *ops = chip->controller->ops;
|
||||
u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { };
|
||||
int ret;
|
||||
|
||||
if (!nand_has_setup_data_iface(chip))
|
||||
if (!nand_controller_can_setup_interface(chip))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* A nand_reset_interface() put both the NAND chip and the NAND
|
||||
* controller in timings mode 0. If the default mode for this chip is
|
||||
* also 0, no need to proceed to the change again. Plus, at probe time,
|
||||
* nand_setup_interface() uses ->set/get_features() which would
|
||||
* fail anyway as the parameter page is not available yet.
|
||||
*/
|
||||
if (!chip->best_interface_config)
|
||||
return 0;
|
||||
|
||||
tmode_param[0] = chip->best_interface_config->timings.mode;
|
||||
|
||||
/* Change the mode on the chip side (if supported by the NAND chip) */
|
||||
if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
|
||||
nand_select_target(chip, chipnr);
|
||||
@ -970,14 +979,13 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
|
||||
}
|
||||
|
||||
/* Change the mode on the controller side */
|
||||
ret = chip->controller->ops->setup_data_interface(chip, chipnr,
|
||||
&chip->data_interface);
|
||||
ret = ops->setup_interface(chip, chipnr, chip->best_interface_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Check the mode has been accepted by the chip, if supported */
|
||||
if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE))
|
||||
return 0;
|
||||
goto update_interface_config;
|
||||
|
||||
memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
|
||||
nand_select_target(chip, chipnr);
|
||||
@ -987,12 +995,15 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
|
||||
if (ret)
|
||||
goto err_reset_chip;
|
||||
|
||||
if (tmode_param[0] != chip->onfi_timing_mode_default) {
|
||||
if (tmode_param[0] != chip->best_interface_config->timings.mode) {
|
||||
pr_warn("timing mode %d not acknowledged by the NAND chip\n",
|
||||
chip->onfi_timing_mode_default);
|
||||
chip->best_interface_config->timings.mode);
|
||||
goto err_reset_chip;
|
||||
}
|
||||
|
||||
update_interface_config:
|
||||
chip->current_interface_config = chip->best_interface_config;
|
||||
|
||||
return 0;
|
||||
|
||||
err_reset_chip:
|
||||
@ -1000,7 +1011,7 @@ err_reset_chip:
|
||||
* Fallback to mode 0 if the chip explicitly did not ack the chosen
|
||||
* timing mode.
|
||||
*/
|
||||
nand_reset_data_interface(chip, chipnr);
|
||||
nand_reset_interface(chip, chipnr);
|
||||
nand_select_target(chip, chipnr);
|
||||
nand_reset_op(chip);
|
||||
nand_deselect_target(chip);
|
||||
@ -1009,59 +1020,90 @@ err_reset_chip:
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_init_data_interface - find the best data interface and timings
|
||||
* nand_choose_best_sdr_timings - Pick up the best SDR 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_sdr_timings(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface,
|
||||
struct nand_sdr_timings *spec_timings)
|
||||
{
|
||||
const struct nand_controller_ops *ops = chip->controller->ops;
|
||||
int best_mode = 0, mode, ret;
|
||||
|
||||
iface->type = NAND_SDR_IFACE;
|
||||
|
||||
if (spec_timings) {
|
||||
iface->timings.sdr = *spec_timings;
|
||||
iface->timings.mode = onfi_find_closest_sdr_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->async_timing_mode) - 1;
|
||||
}
|
||||
|
||||
for (mode = best_mode; mode >= 0; mode--) {
|
||||
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, mode);
|
||||
|
||||
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
|
||||
iface);
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
|
||||
chip->best_interface_config = iface;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_choose_interface_config - find the best data interface and timings
|
||||
* @chip: The NAND chip
|
||||
*
|
||||
* Find the best data interface and NAND timings supported by the chip
|
||||
* and the driver.
|
||||
* First tries to retrieve supported timing modes from ONFI information,
|
||||
* and if the NAND chip does not support ONFI, relies on the
|
||||
* ->onfi_timing_mode_default specified in the nand_ids table. After this
|
||||
* function nand_chip->data_interface is initialized with the best timing mode
|
||||
* available.
|
||||
* and the driver. Eventually let the NAND manufacturer driver propose his own
|
||||
* set of timings.
|
||||
*
|
||||
* After this function nand_chip->interface_config is initialized with the best
|
||||
* timing mode available.
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise.
|
||||
*/
|
||||
static int nand_init_data_interface(struct nand_chip *chip)
|
||||
static int nand_choose_interface_config(struct nand_chip *chip)
|
||||
{
|
||||
int modes, mode, ret;
|
||||
struct nand_interface_config *iface;
|
||||
int ret;
|
||||
|
||||
if (!nand_has_setup_data_iface(chip))
|
||||
if (!nand_controller_can_setup_interface(chip))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* First try to identify the best timings from ONFI parameters and
|
||||
* if the NAND does not support ONFI, fallback to the default ONFI
|
||||
* timing mode.
|
||||
*/
|
||||
if (chip->parameters.onfi) {
|
||||
modes = chip->parameters.onfi->async_timing_mode;
|
||||
} else {
|
||||
if (!chip->onfi_timing_mode_default)
|
||||
return 0;
|
||||
iface = kzalloc(sizeof(*iface), GFP_KERNEL);
|
||||
if (!iface)
|
||||
return -ENOMEM;
|
||||
|
||||
modes = GENMASK(chip->onfi_timing_mode_default, 0);
|
||||
}
|
||||
if (chip->ops.choose_interface_config)
|
||||
ret = chip->ops.choose_interface_config(chip, iface);
|
||||
else
|
||||
ret = nand_choose_best_sdr_timings(chip, iface, NULL);
|
||||
|
||||
for (mode = fls(modes) - 1; mode >= 0; mode--) {
|
||||
ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode);
|
||||
if (ret)
|
||||
continue;
|
||||
if (ret)
|
||||
kfree(iface);
|
||||
|
||||
/*
|
||||
* Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the
|
||||
* controller supports the requested timings.
|
||||
*/
|
||||
ret = chip->controller->ops->setup_data_interface(chip,
|
||||
NAND_DATA_IFACE_CHECK_ONLY,
|
||||
&chip->data_interface);
|
||||
if (!ret) {
|
||||
chip->onfi_timing_mode_default = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1122,9 +1164,9 @@ 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)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(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),
|
||||
@ -1166,7 +1208,7 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
|
||||
unsigned int len)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
u8 addrs[5];
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_READ0, 0),
|
||||
@ -1263,7 +1305,7 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(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)),
|
||||
@ -1318,7 +1360,7 @@ int nand_change_read_column_op(struct nand_chip *chip,
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
u8 addrs[2] = {};
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_RNDOUT, 0),
|
||||
@ -1392,9 +1434,9 @@ 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)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u8 addrs[5] = {};
|
||||
struct nand_op_instr instrs[] = {
|
||||
/*
|
||||
@ -1517,7 +1559,7 @@ int nand_prog_page_end_op(struct nand_chip *chip)
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_PAGEPROG,
|
||||
PSEC_TO_NSEC(sdr->tWB_max)),
|
||||
@ -1624,7 +1666,7 @@ int nand_change_write_column_op(struct nand_chip *chip,
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
u8 addrs[2];
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_RNDIN, 0),
|
||||
@ -1679,7 +1721,7 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(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)),
|
||||
@ -1718,7 +1760,7 @@ 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(&chip->data_interface);
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(NAND_CMD_STATUS,
|
||||
PSEC_TO_NSEC(sdr->tADL_min)),
|
||||
@ -1787,7 +1829,7 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(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),
|
||||
@ -1846,7 +1888,7 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(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)),
|
||||
@ -1893,7 +1935,7 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature,
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(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)),
|
||||
@ -1950,7 +1992,7 @@ int nand_reset_op(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(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),
|
||||
@ -2480,17 +2522,16 @@ EXPORT_SYMBOL_GPL(nand_subop_get_data_len);
|
||||
* @chipnr: Internal die id
|
||||
*
|
||||
* Save the timings data structure, then apply SDR timings mode 0 (see
|
||||
* nand_reset_data_interface for details), do the reset operation, and
|
||||
* apply back the previous timings.
|
||||
* nand_reset_interface for details), do the reset operation, and apply
|
||||
* back the previous timings.
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
int nand_reset(struct nand_chip *chip, int chipnr)
|
||||
{
|
||||
struct nand_data_interface saved_data_intf = chip->data_interface;
|
||||
int ret;
|
||||
|
||||
ret = nand_reset_data_interface(chip, chipnr);
|
||||
ret = nand_reset_interface(chip, chipnr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -2505,18 +2546,7 @@ int nand_reset(struct nand_chip *chip, int chipnr)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* A nand_reset_data_interface() put both the NAND chip and the NAND
|
||||
* controller in timings mode 0. If the default mode for this chip is
|
||||
* also 0, no need to proceed to the change again. Plus, at probe time,
|
||||
* nand_setup_data_interface() uses ->set/get_features() which would
|
||||
* fail anyway as the parameter page is not available yet.
|
||||
*/
|
||||
if (!chip->onfi_timing_mode_default)
|
||||
return 0;
|
||||
|
||||
chip->data_interface = saved_data_intf;
|
||||
ret = nand_setup_data_interface(chip, chipnr);
|
||||
ret = nand_setup_interface(chip, chipnr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -3215,10 +3245,10 @@ static int nand_setup_read_retry(struct nand_chip *chip, int retry_mode)
|
||||
if (retry_mode >= chip->read_retries)
|
||||
return -EINVAL;
|
||||
|
||||
if (!chip->setup_read_retry)
|
||||
if (!chip->ops.setup_read_retry)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return chip->setup_read_retry(chip, retry_mode);
|
||||
return chip->ops.setup_read_retry(chip, retry_mode);
|
||||
}
|
||||
|
||||
static void nand_wait_readrdy(struct nand_chip *chip)
|
||||
@ -3228,7 +3258,7 @@ static void nand_wait_readrdy(struct nand_chip *chip)
|
||||
if (!(chip->options & NAND_NEED_READRDY))
|
||||
return;
|
||||
|
||||
sdr = nand_get_sdr_timings(&chip->data_interface);
|
||||
sdr = nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
WARN_ON(nand_wait_rdy_op(chip, PSEC_TO_MSEC(sdr->tR_max), 0));
|
||||
}
|
||||
|
||||
@ -4462,8 +4492,8 @@ static int nand_suspend(struct mtd_info *mtd)
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
if (chip->suspend)
|
||||
ret = chip->suspend(chip);
|
||||
if (chip->ops.suspend)
|
||||
ret = chip->ops.suspend(chip);
|
||||
if (!ret)
|
||||
chip->suspended = 1;
|
||||
mutex_unlock(&chip->lock);
|
||||
@ -4481,8 +4511,8 @@ static void nand_resume(struct mtd_info *mtd)
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
if (chip->suspended) {
|
||||
if (chip->resume)
|
||||
chip->resume(chip);
|
||||
if (chip->ops.resume)
|
||||
chip->ops.resume(chip);
|
||||
chip->suspended = 0;
|
||||
} else {
|
||||
pr_err("%s called for a chip which is not in suspended state\n",
|
||||
@ -4511,10 +4541,10 @@ static int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if (!chip->lock_area)
|
||||
if (!chip->ops.lock_area)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return chip->lock_area(chip, ofs, len);
|
||||
return chip->ops.lock_area(chip, ofs, len);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4527,10 +4557,10 @@ static int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if (!chip->unlock_area)
|
||||
if (!chip->ops.unlock_area)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return chip->unlock_area(chip, ofs, len);
|
||||
return chip->ops.unlock_area(chip, ofs, len);
|
||||
}
|
||||
|
||||
/* Set default functions */
|
||||
@ -4743,8 +4773,6 @@ static bool find_full_id_nand(struct nand_chip *chip,
|
||||
chip->options |= type->options;
|
||||
chip->base.eccreq.strength = NAND_ECC_STRENGTH(type);
|
||||
chip->base.eccreq.step_size = NAND_ECC_STEP(type);
|
||||
chip->onfi_timing_mode_default =
|
||||
type->onfi_timing_mode_default;
|
||||
|
||||
chip->parameters.model = kstrdup(type->name, GFP_KERNEL);
|
||||
if (!chip->parameters.model)
|
||||
@ -4810,9 +4838,9 @@ static void nand_manufacturer_cleanup(struct nand_chip *chip)
|
||||
}
|
||||
|
||||
static const char *
|
||||
nand_manufacturer_name(const struct nand_manufacturer *manufacturer)
|
||||
nand_manufacturer_name(const struct nand_manufacturer_desc *manufacturer_desc)
|
||||
{
|
||||
return manufacturer ? manufacturer->name : "Unknown";
|
||||
return manufacturer_desc ? manufacturer_desc->name : "Unknown";
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4820,7 +4848,7 @@ nand_manufacturer_name(const struct nand_manufacturer *manufacturer)
|
||||
*/
|
||||
static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
|
||||
{
|
||||
const struct nand_manufacturer *manufacturer;
|
||||
const struct nand_manufacturer_desc *manufacturer_desc;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_memory_organization *memorg;
|
||||
int busw, ret;
|
||||
@ -4877,8 +4905,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
|
||||
chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data));
|
||||
|
||||
/* Try to identify manufacturer */
|
||||
manufacturer = nand_get_manufacturer(maf_id);
|
||||
chip->manufacturer.desc = manufacturer;
|
||||
manufacturer_desc = nand_get_manufacturer_desc(maf_id);
|
||||
chip->manufacturer.desc = manufacturer_desc;
|
||||
|
||||
if (!type)
|
||||
type = nand_flash_ids;
|
||||
@ -4957,7 +4985,7 @@ ident_done:
|
||||
*/
|
||||
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
|
||||
maf_id, dev_id);
|
||||
pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
|
||||
pr_info("%s %s\n", nand_manufacturer_name(manufacturer_desc),
|
||||
mtd->name);
|
||||
pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8,
|
||||
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8);
|
||||
@ -4992,7 +5020,7 @@ ident_done:
|
||||
|
||||
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
|
||||
maf_id, dev_id);
|
||||
pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
|
||||
pr_info("%s %s\n", nand_manufacturer_name(manufacturer_desc),
|
||||
chip->parameters.model);
|
||||
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
|
||||
(int)(targetsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
|
||||
@ -5185,7 +5213,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
|
||||
mutex_init(&chip->lock);
|
||||
|
||||
/* Enforce the right timings for reset/detection */
|
||||
onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
|
||||
chip->current_interface_config = nand_get_reset_interface_config();
|
||||
|
||||
ret = nand_dt_init(chip);
|
||||
if (ret)
|
||||
@ -5972,16 +6000,16 @@ static int nand_scan_tail(struct nand_chip *chip)
|
||||
if (!mtd->bitflip_threshold)
|
||||
mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
|
||||
|
||||
/* Initialize the ->data_interface field. */
|
||||
ret = nand_init_data_interface(chip);
|
||||
/* Find the fastest data interface for this chip */
|
||||
ret = nand_choose_interface_config(chip);
|
||||
if (ret)
|
||||
goto err_nanddev_cleanup;
|
||||
|
||||
/* Enter fastest possible mode on all dies. */
|
||||
for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
|
||||
ret = nand_setup_data_interface(chip, i);
|
||||
ret = nand_setup_interface(chip, i);
|
||||
if (ret)
|
||||
goto err_nanddev_cleanup;
|
||||
goto err_free_interface_config;
|
||||
}
|
||||
|
||||
/* Check, if we should skip the bad block table scan */
|
||||
@ -5991,10 +6019,12 @@ static int nand_scan_tail(struct nand_chip *chip)
|
||||
/* Build bad block table */
|
||||
ret = nand_create_bbt(chip);
|
||||
if (ret)
|
||||
goto err_nanddev_cleanup;
|
||||
goto err_free_interface_config;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_interface_config:
|
||||
kfree(chip->best_interface_config);
|
||||
|
||||
err_nanddev_cleanup:
|
||||
nanddev_cleanup(&chip->base);
|
||||
@ -6088,6 +6118,9 @@ void nand_cleanup(struct nand_chip *chip)
|
||||
& NAND_BBT_DYNAMICSTRUCT)
|
||||
kfree(chip->badblock_pattern);
|
||||
|
||||
/* Free the data interface */
|
||||
kfree(chip->best_interface_config);
|
||||
|
||||
/* Free manufacturer priv data. */
|
||||
nand_manufacturer_cleanup(chip);
|
||||
|
||||
|
@ -1226,7 +1226,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* If no primary table decriptor is given, scan the device to build a
|
||||
* If no primary table descriptor is given, scan the device to build a
|
||||
* memory based bad block table.
|
||||
*/
|
||||
if (!td) {
|
||||
|
@ -337,7 +337,7 @@ static int hynix_mlc_1xnm_rr_init(struct nand_chip *chip,
|
||||
rr->nregs = nregs;
|
||||
rr->regs = hynix_1xnm_mlc_read_retry_regs;
|
||||
hynix->read_retry = rr;
|
||||
chip->setup_read_retry = hynix_nand_setup_read_retry;
|
||||
chip->ops.setup_read_retry = hynix_nand_setup_read_retry;
|
||||
chip->read_retries = nmodes;
|
||||
|
||||
out:
|
||||
@ -673,6 +673,15 @@ static void hynix_nand_cleanup(struct nand_chip *chip)
|
||||
nand_set_manufacturer_data(chip, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
h27ucg8t2atrbc_choose_interface_config(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface)
|
||||
{
|
||||
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 4);
|
||||
|
||||
return nand_choose_best_sdr_timings(chip, iface, NULL);
|
||||
}
|
||||
|
||||
static int hynix_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
struct hynix_nand *hynix;
|
||||
@ -689,6 +698,11 @@ static int hynix_nand_init(struct nand_chip *chip)
|
||||
|
||||
nand_set_manufacturer_data(chip, hynix);
|
||||
|
||||
if (!strncmp("H27UCG8T2ATR-BC", chip->parameters.model,
|
||||
sizeof("H27UCG8T2ATR-BC") - 1))
|
||||
chip->ops.choose_interface_config =
|
||||
h27ucg8t2atrbc_choose_interface_config;
|
||||
|
||||
ret = hynix_nand_rr_init(chip);
|
||||
if (ret)
|
||||
hynix_nand_cleanup(chip);
|
||||
|
@ -28,8 +28,7 @@ struct nand_flash_dev nand_flash_ids[] = {
|
||||
*/
|
||||
{"TC58NVG0S3E 1G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
|
||||
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512),
|
||||
2 },
|
||||
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
|
||||
{"TC58NVG2S0F 4G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
||||
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
|
||||
@ -51,7 +50,10 @@ struct nand_flash_dev nand_flash_ids[] = {
|
||||
{"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
|
||||
{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
|
||||
SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
|
||||
NAND_ECC_INFO(40, SZ_1K), 4 },
|
||||
NAND_ECC_INFO(40, SZ_1K) },
|
||||
{"TH58NVG2S3HBAI4 4G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xdc, 0x91, 0x15, 0x76} },
|
||||
SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) },
|
||||
|
||||
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
|
||||
@ -166,7 +168,7 @@ struct nand_flash_dev nand_flash_ids[] = {
|
||||
};
|
||||
|
||||
/* Manufacturer IDs */
|
||||
static const struct nand_manufacturer nand_manufacturers[] = {
|
||||
static const struct nand_manufacturer_desc nand_manufacturer_descs[] = {
|
||||
{NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops},
|
||||
{NAND_MFR_ATO, "ATO"},
|
||||
{NAND_MFR_EON, "Eon"},
|
||||
@ -186,20 +188,20 @@ static const struct nand_manufacturer nand_manufacturers[] = {
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_get_manufacturer - Get manufacturer information from the manufacturer
|
||||
* ID
|
||||
* nand_get_manufacturer_desc - Get manufacturer information from the
|
||||
* manufacturer ID
|
||||
* @id: manufacturer ID
|
||||
*
|
||||
* Returns a pointer a nand_manufacturer object if the manufacturer is defined
|
||||
* Returns a nand_manufacturer_desc object if the manufacturer is defined
|
||||
* in the NAND manufacturers database, NULL otherwise.
|
||||
*/
|
||||
const struct nand_manufacturer *nand_get_manufacturer(u8 id)
|
||||
const struct nand_manufacturer_desc *nand_get_manufacturer_desc(u8 id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nand_manufacturers); i++)
|
||||
if (nand_manufacturers[i].id == id)
|
||||
return &nand_manufacturers[i];
|
||||
for (i = 0; i < ARRAY_SIZE(nand_manufacturer_descs); i++)
|
||||
if (nand_manufacturer_descs[i].id == id)
|
||||
return &nand_manufacturer_descs[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -354,6 +354,9 @@ static void nand_command(struct nand_chip *chip, unsigned int command,
|
||||
|
||||
static void nand_ccs_delay(struct nand_chip *chip)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
|
||||
/*
|
||||
* The controller already takes care of waiting for tCCS when the RNDIN
|
||||
* or RNDOUT command is sent, return directly.
|
||||
@ -365,8 +368,8 @@ 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_has_setup_data_iface(chip))
|
||||
ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000);
|
||||
if (nand_controller_can_setup_interface(chip))
|
||||
ndelay(sdr->tCCS_min / 1000);
|
||||
else
|
||||
ndelay(500);
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ static void macronix_nand_onfi_init(struct nand_chip *chip)
|
||||
return;
|
||||
|
||||
chip->read_retries = MACRONIX_NUM_READ_RETRY_MODES;
|
||||
chip->setup_read_retry = macronix_nand_setup_read_retry;
|
||||
chip->ops.setup_read_retry = macronix_nand_setup_read_retry;
|
||||
|
||||
if (p->supports_set_get_features) {
|
||||
bitmap_set(p->set_feature_list,
|
||||
@ -242,8 +242,8 @@ static void macronix_nand_block_protection_support(struct nand_chip *chip)
|
||||
bitmap_set(chip->parameters.set_feature_list,
|
||||
ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1);
|
||||
|
||||
chip->lock_area = mxic_nand_lock;
|
||||
chip->unlock_area = mxic_nand_unlock;
|
||||
chip->ops.lock_area = mxic_nand_lock;
|
||||
chip->ops.unlock_area = mxic_nand_unlock;
|
||||
}
|
||||
|
||||
static int nand_power_down_op(struct nand_chip *chip)
|
||||
@ -312,8 +312,8 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip)
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
chip->suspend = mxic_nand_suspend;
|
||||
chip->resume = mxic_nand_resume;
|
||||
chip->ops.suspend = mxic_nand_suspend;
|
||||
chip->ops.resume = mxic_nand_resume;
|
||||
}
|
||||
|
||||
static int macronix_nand_init(struct nand_chip *chip)
|
||||
|
@ -84,7 +84,7 @@ static int micron_nand_onfi_init(struct nand_chip *chip)
|
||||
struct nand_onfi_vendor_micron *micron = (void *)p->onfi->vendor;
|
||||
|
||||
chip->read_retries = micron->read_retry_options;
|
||||
chip->setup_read_retry = micron_nand_setup_read_retry;
|
||||
chip->ops.setup_read_retry = micron_nand_setup_read_retry;
|
||||
}
|
||||
|
||||
if (p->supports_set_get_features) {
|
||||
|
@ -12,7 +12,14 @@
|
||||
|
||||
#define ONFI_DYN_TIMING_MAX U16_MAX
|
||||
|
||||
static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
/*
|
||||
* For non-ONFI chips we use the highest possible value for tPROG and tBERS.
|
||||
* tR and tCCS will take the default values precised in the ONFI specification
|
||||
* for timing mode 0, respectively 200us and 500ns.
|
||||
*
|
||||
* These four values are tweaked to be more accurate in the case of ONFI chips.
|
||||
*/
|
||||
static const struct nand_interface_config onfi_sdr_timings[] = {
|
||||
/* Mode 0 */
|
||||
{
|
||||
.type = NAND_SDR_IFACE,
|
||||
@ -20,6 +27,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 20000,
|
||||
.tALS_min = 50000,
|
||||
@ -63,6 +72,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 25000,
|
||||
@ -106,6 +117,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 15000,
|
||||
@ -149,6 +162,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
@ -192,6 +207,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
@ -235,6 +252,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
.timings.sdr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tADL_min = 400000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
@ -273,23 +292,79 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* onfi_fill_data_interface - [NAND Interface] Initialize a data interface from
|
||||
* given ONFI mode
|
||||
* @mode: The ONFI timing mode
|
||||
*/
|
||||
int onfi_fill_data_interface(struct nand_chip *chip,
|
||||
enum nand_data_interface_type type,
|
||||
int timing_mode)
|
||||
/* All NAND chips share the same reset data interface: SDR mode 0 */
|
||||
const struct nand_interface_config *nand_get_reset_interface_config(void)
|
||||
{
|
||||
return &onfi_sdr_timings[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* onfi_find_closest_sdr_mode - Derive the closest ONFI SDR timing mode given a
|
||||
* set of timings
|
||||
* @spec_timings: the timings to challenge
|
||||
*/
|
||||
unsigned int
|
||||
onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings)
|
||||
{
|
||||
const struct nand_sdr_timings *onfi_timings;
|
||||
int mode;
|
||||
|
||||
for (mode = ARRAY_SIZE(onfi_sdr_timings) - 1; mode > 0; mode--) {
|
||||
onfi_timings = &onfi_sdr_timings[mode].timings.sdr;
|
||||
|
||||
if (spec_timings->tCCS_min <= onfi_timings->tCCS_min &&
|
||||
spec_timings->tADL_min <= onfi_timings->tADL_min &&
|
||||
spec_timings->tALH_min <= onfi_timings->tALH_min &&
|
||||
spec_timings->tALS_min <= onfi_timings->tALS_min &&
|
||||
spec_timings->tAR_min <= onfi_timings->tAR_min &&
|
||||
spec_timings->tCEH_min <= onfi_timings->tCEH_min &&
|
||||
spec_timings->tCH_min <= onfi_timings->tCH_min &&
|
||||
spec_timings->tCLH_min <= onfi_timings->tCLH_min &&
|
||||
spec_timings->tCLR_min <= onfi_timings->tCLR_min &&
|
||||
spec_timings->tCLS_min <= onfi_timings->tCLS_min &&
|
||||
spec_timings->tCOH_min <= onfi_timings->tCOH_min &&
|
||||
spec_timings->tCS_min <= onfi_timings->tCS_min &&
|
||||
spec_timings->tDH_min <= onfi_timings->tDH_min &&
|
||||
spec_timings->tDS_min <= onfi_timings->tDS_min &&
|
||||
spec_timings->tIR_min <= onfi_timings->tIR_min &&
|
||||
spec_timings->tRC_min <= onfi_timings->tRC_min &&
|
||||
spec_timings->tREH_min <= onfi_timings->tREH_min &&
|
||||
spec_timings->tRHOH_min <= onfi_timings->tRHOH_min &&
|
||||
spec_timings->tRHW_min <= onfi_timings->tRHW_min &&
|
||||
spec_timings->tRLOH_min <= onfi_timings->tRLOH_min &&
|
||||
spec_timings->tRP_min <= onfi_timings->tRP_min &&
|
||||
spec_timings->tRR_min <= onfi_timings->tRR_min &&
|
||||
spec_timings->tWC_min <= onfi_timings->tWC_min &&
|
||||
spec_timings->tWH_min <= onfi_timings->tWH_min &&
|
||||
spec_timings->tWHR_min <= onfi_timings->tWHR_min &&
|
||||
spec_timings->tWP_min <= onfi_timings->tWP_min &&
|
||||
spec_timings->tWW_min <= onfi_timings->tWW_min)
|
||||
return mode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
struct nand_data_interface *iface = &chip->data_interface;
|
||||
struct onfi_params *onfi = chip->parameters.onfi;
|
||||
|
||||
if (type != NAND_SDR_IFACE)
|
||||
return -EINVAL;
|
||||
if (WARN_ON(type != NAND_SDR_IFACE))
|
||||
return;
|
||||
|
||||
if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_sdr_timings)))
|
||||
return;
|
||||
|
||||
*iface = onfi_sdr_timings[timing_mode];
|
||||
|
||||
@ -308,22 +383,5 @@ int onfi_fill_data_interface(struct nand_chip *chip,
|
||||
|
||||
/* nanoseconds -> picoseconds */
|
||||
timings->tCCS_min = 1000UL * onfi->tCCS;
|
||||
} else {
|
||||
struct nand_sdr_timings *timings = &iface->timings.sdr;
|
||||
/*
|
||||
* For non-ONFI chips we use the highest possible value for
|
||||
* tPROG and tBERS. tR and tCCS will take the default values
|
||||
* precised in the ONFI specification for timing mode 0,
|
||||
* respectively 200us and 500ns.
|
||||
*/
|
||||
|
||||
/* microseconds -> picoseconds */
|
||||
timings->tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX;
|
||||
timings->tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX;
|
||||
|
||||
timings->tR_max = 200000000;
|
||||
timings->tCCS_min = 500000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ static int toshiba_nand_benand_read_eccstatus_op(struct nand_chip *chip,
|
||||
|
||||
if (nand_has_exec_op(chip)) {
|
||||
const struct nand_sdr_timings *sdr =
|
||||
nand_get_sdr_timings(&chip->data_interface);
|
||||
nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_CMD(TOSHIBA_NAND_CMD_ECC_STATUS_READ,
|
||||
PSEC_TO_NSEC(sdr->tADL_min)),
|
||||
@ -194,17 +194,79 @@ static void toshiba_nand_decode_id(struct nand_chip *chip)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tc58teg5dclta00_choose_interface_config(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface)
|
||||
{
|
||||
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 5);
|
||||
|
||||
return nand_choose_best_sdr_timings(chip, iface, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
tc58nvg0s3e_choose_interface_config(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface)
|
||||
{
|
||||
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 2);
|
||||
|
||||
return nand_choose_best_sdr_timings(chip, iface, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
th58nvg2s3hbai4_choose_interface_config(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface)
|
||||
{
|
||||
struct nand_sdr_timings *sdr = &iface->timings.sdr;
|
||||
|
||||
/* Start with timings from the closest timing mode, mode 4. */
|
||||
onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 4);
|
||||
|
||||
/* Patch timings that differ from mode 4. */
|
||||
sdr->tALS_min = 12000;
|
||||
sdr->tCHZ_max = 20000;
|
||||
sdr->tCLS_min = 12000;
|
||||
sdr->tCOH_min = 0;
|
||||
sdr->tDS_min = 12000;
|
||||
sdr->tRHOH_min = 25000;
|
||||
sdr->tRHW_min = 30000;
|
||||
sdr->tRHZ_max = 60000;
|
||||
sdr->tWHR_min = 60000;
|
||||
|
||||
/* Patch timings not part of onfi timing mode. */
|
||||
sdr->tPROG_max = 700000000;
|
||||
sdr->tBERS_max = 5000000000;
|
||||
|
||||
return nand_choose_best_sdr_timings(chip, iface, sdr);
|
||||
}
|
||||
|
||||
static int tc58teg5dclta00_init(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
chip->onfi_timing_mode_default = 5;
|
||||
chip->ops.choose_interface_config =
|
||||
&tc58teg5dclta00_choose_interface_config;
|
||||
chip->options |= NAND_NEED_SCRAMBLING;
|
||||
mtd_set_pairing_scheme(mtd, &dist3_pairing_scheme);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc58nvg0s3e_init(struct nand_chip *chip)
|
||||
{
|
||||
chip->ops.choose_interface_config =
|
||||
&tc58nvg0s3e_choose_interface_config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int th58nvg2s3hbai4_init(struct nand_chip *chip)
|
||||
{
|
||||
chip->ops.choose_interface_config =
|
||||
&th58nvg2s3hbai4_choose_interface_config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int toshiba_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_is_slc(chip))
|
||||
@ -217,6 +279,12 @@ static int toshiba_nand_init(struct nand_chip *chip)
|
||||
|
||||
if (!strcmp("TC58TEG5DCLTA00", chip->parameters.model))
|
||||
tc58teg5dclta00_init(chip);
|
||||
if (!strncmp("TC58NVG0S3E", chip->parameters.model,
|
||||
sizeof("TC58NVG0S3E") - 1))
|
||||
tc58nvg0s3e_init(chip);
|
||||
if (!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
|
||||
sizeof("TH58NVG2S3HBAI4") - 1))
|
||||
th58nvg2s3hbai4_init(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Error Location Module
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "omap-elm"
|
||||
|
@ -459,11 +459,13 @@ struct qcom_nand_host {
|
||||
* among different NAND controllers.
|
||||
* @ecc_modes - ecc mode for NAND
|
||||
* @is_bam - whether NAND controller is using BAM
|
||||
* @is_qpic - whether NAND CTRL is part of qpic IP
|
||||
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
|
||||
*/
|
||||
struct qcom_nandc_props {
|
||||
u32 ecc_modes;
|
||||
bool is_bam;
|
||||
bool is_qpic;
|
||||
u32 dev_cmd_reg_start;
|
||||
};
|
||||
|
||||
@ -2774,14 +2776,24 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
|
||||
u32 nand_ctrl;
|
||||
|
||||
/* kill onenand */
|
||||
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
|
||||
if (!nandc->props->is_qpic)
|
||||
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
|
||||
nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
|
||||
NAND_DEV_CMD_VLD_VAL);
|
||||
|
||||
/* enable ADM or BAM DMA */
|
||||
if (nandc->props->is_bam) {
|
||||
nand_ctrl = nandc_read(nandc, NAND_CTRL);
|
||||
nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN);
|
||||
|
||||
/*
|
||||
*NAND_CTRL is an operational registers, and CPU
|
||||
* access to operational registers are read only
|
||||
* in BAM mode. So update the NAND_CTRL register
|
||||
* only if it is not in BAM mode. In most cases BAM
|
||||
* mode will be enabled in bootloader
|
||||
*/
|
||||
if (!(nand_ctrl & BAM_MODE_EN))
|
||||
nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN);
|
||||
} else {
|
||||
nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
|
||||
}
|
||||
@ -3035,12 +3047,14 @@ static const struct qcom_nandc_props ipq806x_nandc_props = {
|
||||
static const struct qcom_nandc_props ipq4019_nandc_props = {
|
||||
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
|
||||
.is_bam = true,
|
||||
.is_qpic = true,
|
||||
.dev_cmd_reg_start = 0x0,
|
||||
};
|
||||
|
||||
static const struct qcom_nandc_props ipq8074_nandc_props = {
|
||||
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
|
||||
.is_bam = true,
|
||||
.is_qpic = true,
|
||||
.dev_cmd_reg_start = 0x7000,
|
||||
};
|
||||
|
||||
|
@ -808,8 +808,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int s3c2410_nand_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int s3c2410_nand_setup_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
@ -999,7 +999,7 @@ static int s3c2410_nand_attach_chip(struct nand_chip *chip)
|
||||
|
||||
static const struct nand_controller_ops s3c24xx_nand_controller_ops = {
|
||||
.attach_chip = s3c2410_nand_attach_chip,
|
||||
.setup_data_interface = s3c2410_nand_setup_data_interface,
|
||||
.setup_interface = s3c2410_nand_setup_interface,
|
||||
};
|
||||
|
||||
static const struct of_device_id s3c24xx_nand_dt_ids[] = {
|
||||
|
@ -11,10 +11,13 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
/* Bad block marker length */
|
||||
@ -242,7 +245,8 @@ struct stm32_fmc2_nfc {
|
||||
struct nand_controller base;
|
||||
struct stm32_fmc2_nand nand;
|
||||
struct device *dev;
|
||||
void __iomem *io_base;
|
||||
struct device *cdev;
|
||||
struct regmap *regmap;
|
||||
void __iomem *data_base[FMC2_MAX_CE];
|
||||
void __iomem *cmd_base[FMC2_MAX_CE];
|
||||
void __iomem *addr_base[FMC2_MAX_CE];
|
||||
@ -277,40 +281,37 @@ static void stm32_fmc2_nfc_timings_init(struct nand_chip *chip)
|
||||
struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
|
||||
struct stm32_fmc2_nand *nand = to_fmc2_nand(chip);
|
||||
struct stm32_fmc2_timings *timings = &nand->timings;
|
||||
u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR);
|
||||
u32 pmem, patt;
|
||||
|
||||
/* Set tclr/tar timings */
|
||||
pcr &= ~FMC2_PCR_TCLR;
|
||||
pcr |= FIELD_PREP(FMC2_PCR_TCLR, timings->tclr);
|
||||
pcr &= ~FMC2_PCR_TAR;
|
||||
pcr |= FIELD_PREP(FMC2_PCR_TAR, timings->tar);
|
||||
regmap_update_bits(nfc->regmap, FMC2_PCR,
|
||||
FMC2_PCR_TCLR | FMC2_PCR_TAR,
|
||||
FIELD_PREP(FMC2_PCR_TCLR, timings->tclr) |
|
||||
FIELD_PREP(FMC2_PCR_TAR, timings->tar));
|
||||
|
||||
/* Set tset/twait/thold/thiz timings in common bank */
|
||||
pmem = FIELD_PREP(FMC2_PMEM_MEMSET, timings->tset_mem);
|
||||
pmem |= FIELD_PREP(FMC2_PMEM_MEMWAIT, timings->twait);
|
||||
pmem |= FIELD_PREP(FMC2_PMEM_MEMHOLD, timings->thold_mem);
|
||||
pmem |= FIELD_PREP(FMC2_PMEM_MEMHIZ, timings->thiz);
|
||||
regmap_write(nfc->regmap, FMC2_PMEM, pmem);
|
||||
|
||||
/* Set tset/twait/thold/thiz timings in attribut bank */
|
||||
patt = FIELD_PREP(FMC2_PATT_ATTSET, timings->tset_att);
|
||||
patt |= FIELD_PREP(FMC2_PATT_ATTWAIT, timings->twait);
|
||||
patt |= FIELD_PREP(FMC2_PATT_ATTHOLD, timings->thold_att);
|
||||
patt |= FIELD_PREP(FMC2_PATT_ATTHIZ, timings->thiz);
|
||||
|
||||
writel_relaxed(pcr, nfc->io_base + FMC2_PCR);
|
||||
writel_relaxed(pmem, nfc->io_base + FMC2_PMEM);
|
||||
writel_relaxed(patt, nfc->io_base + FMC2_PATT);
|
||||
regmap_write(nfc->regmap, FMC2_PATT, patt);
|
||||
}
|
||||
|
||||
static void stm32_fmc2_nfc_setup(struct nand_chip *chip)
|
||||
{
|
||||
struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
|
||||
u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR);
|
||||
u32 pcr = 0, pcr_mask;
|
||||
|
||||
/* Configure ECC algorithm (default configuration is Hamming) */
|
||||
pcr &= ~FMC2_PCR_ECCALG;
|
||||
pcr &= ~FMC2_PCR_BCHECC;
|
||||
pcr_mask = FMC2_PCR_ECCALG;
|
||||
pcr_mask |= FMC2_PCR_BCHECC;
|
||||
if (chip->ecc.strength == FMC2_ECC_BCH8) {
|
||||
pcr |= FMC2_PCR_ECCALG;
|
||||
pcr |= FMC2_PCR_BCHECC;
|
||||
@ -319,15 +320,15 @@ static void stm32_fmc2_nfc_setup(struct nand_chip *chip)
|
||||
}
|
||||
|
||||
/* Set buswidth */
|
||||
pcr &= ~FMC2_PCR_PWID;
|
||||
pcr_mask |= FMC2_PCR_PWID;
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
pcr |= FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16);
|
||||
|
||||
/* Set ECC sector size */
|
||||
pcr &= ~FMC2_PCR_ECCSS;
|
||||
pcr_mask |= FMC2_PCR_ECCSS;
|
||||
pcr |= FIELD_PREP(FMC2_PCR_ECCSS, FMC2_PCR_ECCSS_512);
|
||||
|
||||
writel_relaxed(pcr, nfc->io_base + FMC2_PCR);
|
||||
regmap_update_bits(nfc->regmap, FMC2_PCR, pcr_mask, pcr);
|
||||
}
|
||||
|
||||
static int stm32_fmc2_nfc_select_chip(struct nand_chip *chip, int chipnr)
|
||||
@ -393,81 +394,63 @@ static int stm32_fmc2_nfc_select_chip(struct nand_chip *chip, int chipnr)
|
||||
|
||||
static void stm32_fmc2_nfc_set_buswidth_16(struct stm32_fmc2_nfc *nfc, bool set)
|
||||
{
|
||||
u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR);
|
||||
u32 pcr;
|
||||
|
||||
pcr &= ~FMC2_PCR_PWID;
|
||||
if (set)
|
||||
pcr |= FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16);
|
||||
writel_relaxed(pcr, nfc->io_base + FMC2_PCR);
|
||||
pcr = set ? FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16) :
|
||||
FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_8);
|
||||
|
||||
regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_PWID, pcr);
|
||||
}
|
||||
|
||||
static void stm32_fmc2_nfc_set_ecc(struct stm32_fmc2_nfc *nfc, bool enable)
|
||||
{
|
||||
u32 pcr = readl(nfc->io_base + FMC2_PCR);
|
||||
|
||||
pcr &= ~FMC2_PCR_ECCEN;
|
||||
if (enable)
|
||||
pcr |= FMC2_PCR_ECCEN;
|
||||
writel(pcr, nfc->io_base + FMC2_PCR);
|
||||
regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_ECCEN,
|
||||
enable ? FMC2_PCR_ECCEN : 0);
|
||||
}
|
||||
|
||||
static inline void stm32_fmc2_nfc_enable_seq_irq(struct stm32_fmc2_nfc *nfc)
|
||||
static void stm32_fmc2_nfc_enable_seq_irq(struct stm32_fmc2_nfc *nfc)
|
||||
{
|
||||
u32 csqier = readl_relaxed(nfc->io_base + FMC2_CSQIER);
|
||||
|
||||
csqier |= FMC2_CSQIER_TCIE;
|
||||
|
||||
nfc->irq_state = FMC2_IRQ_SEQ;
|
||||
|
||||
writel_relaxed(csqier, nfc->io_base + FMC2_CSQIER);
|
||||
regmap_update_bits(nfc->regmap, FMC2_CSQIER,
|
||||
FMC2_CSQIER_TCIE, FMC2_CSQIER_TCIE);
|
||||
}
|
||||
|
||||
static inline void stm32_fmc2_nfc_disable_seq_irq(struct stm32_fmc2_nfc *nfc)
|
||||
static void stm32_fmc2_nfc_disable_seq_irq(struct stm32_fmc2_nfc *nfc)
|
||||
{
|
||||
u32 csqier = readl_relaxed(nfc->io_base + FMC2_CSQIER);
|
||||
|
||||
csqier &= ~FMC2_CSQIER_TCIE;
|
||||
|
||||
writel_relaxed(csqier, nfc->io_base + FMC2_CSQIER);
|
||||
regmap_update_bits(nfc->regmap, FMC2_CSQIER, FMC2_CSQIER_TCIE, 0);
|
||||
|
||||
nfc->irq_state = FMC2_IRQ_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline void stm32_fmc2_nfc_clear_seq_irq(struct stm32_fmc2_nfc *nfc)
|
||||
static void stm32_fmc2_nfc_clear_seq_irq(struct stm32_fmc2_nfc *nfc)
|
||||
{
|
||||
writel_relaxed(FMC2_CSQICR_CLEAR_IRQ, nfc->io_base + FMC2_CSQICR);
|
||||
regmap_write(nfc->regmap, FMC2_CSQICR, FMC2_CSQICR_CLEAR_IRQ);
|
||||
}
|
||||
|
||||
static inline void stm32_fmc2_nfc_enable_bch_irq(struct stm32_fmc2_nfc *nfc,
|
||||
int mode)
|
||||
static void stm32_fmc2_nfc_enable_bch_irq(struct stm32_fmc2_nfc *nfc, int mode)
|
||||
{
|
||||
u32 bchier = readl_relaxed(nfc->io_base + FMC2_BCHIER);
|
||||
|
||||
if (mode == NAND_ECC_WRITE)
|
||||
bchier |= FMC2_BCHIER_EPBRIE;
|
||||
else
|
||||
bchier |= FMC2_BCHIER_DERIE;
|
||||
|
||||
nfc->irq_state = FMC2_IRQ_BCH;
|
||||
|
||||
writel_relaxed(bchier, nfc->io_base + FMC2_BCHIER);
|
||||
if (mode == NAND_ECC_WRITE)
|
||||
regmap_update_bits(nfc->regmap, FMC2_BCHIER,
|
||||
FMC2_BCHIER_EPBRIE, FMC2_BCHIER_EPBRIE);
|
||||
else
|
||||
regmap_update_bits(nfc->regmap, FMC2_BCHIER,
|
||||
FMC2_BCHIER_DERIE, FMC2_BCHIER_DERIE);
|
||||
}
|
||||
|
||||
static inline void stm32_fmc2_nfc_disable_bch_irq(struct stm32_fmc2_nfc *nfc)
|
||||
static void stm32_fmc2_nfc_disable_bch_irq(struct stm32_fmc2_nfc *nfc)
|
||||
{
|
||||
u32 bchier = readl_relaxed(nfc->io_base + FMC2_BCHIER);
|
||||
|
||||
bchier &= ~FMC2_BCHIER_DERIE;
|
||||
bchier &= ~FMC2_BCHIER_EPBRIE;
|
||||
|
||||
writel_relaxed(bchier, nfc->io_base + FMC2_BCHIER);
|
||||
regmap_update_bits(nfc->regmap, FMC2_BCHIER,
|
||||
FMC2_BCHIER_DERIE | FMC2_BCHIER_EPBRIE, 0);
|
||||
|
||||
nfc->irq_state = FMC2_IRQ_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline void stm32_fmc2_nfc_clear_bch_irq(struct stm32_fmc2_nfc *nfc)
|
||||
static void stm32_fmc2_nfc_clear_bch_irq(struct stm32_fmc2_nfc *nfc)
|
||||
{
|
||||
writel_relaxed(FMC2_BCHICR_CLEAR_IRQ, nfc->io_base + FMC2_BCHICR);
|
||||
regmap_write(nfc->regmap, FMC2_BCHICR, FMC2_BCHICR_CLEAR_IRQ);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -481,13 +464,8 @@ static void stm32_fmc2_nfc_hwctl(struct nand_chip *chip, int mode)
|
||||
stm32_fmc2_nfc_set_ecc(nfc, false);
|
||||
|
||||
if (chip->ecc.strength != FMC2_ECC_HAM) {
|
||||
u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR);
|
||||
|
||||
if (mode == NAND_ECC_WRITE)
|
||||
pcr |= FMC2_PCR_WEN;
|
||||
else
|
||||
pcr &= ~FMC2_PCR_WEN;
|
||||
writel_relaxed(pcr, nfc->io_base + FMC2_PCR);
|
||||
regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_WEN,
|
||||
mode == NAND_ECC_WRITE ? FMC2_PCR_WEN : 0);
|
||||
|
||||
reinit_completion(&nfc->complete);
|
||||
stm32_fmc2_nfc_clear_bch_irq(nfc);
|
||||
@ -502,7 +480,7 @@ static void stm32_fmc2_nfc_hwctl(struct nand_chip *chip, int mode)
|
||||
* ECC is 3 bytes for 512 bytes of data (supports error correction up to
|
||||
* max of 1-bit)
|
||||
*/
|
||||
static inline void stm32_fmc2_nfc_ham_set_ecc(const u32 ecc_sta, u8 *ecc)
|
||||
static void stm32_fmc2_nfc_ham_set_ecc(const u32 ecc_sta, u8 *ecc)
|
||||
{
|
||||
ecc[0] = ecc_sta;
|
||||
ecc[1] = ecc_sta >> 8;
|
||||
@ -516,15 +494,15 @@ static int stm32_fmc2_nfc_ham_calculate(struct nand_chip *chip, const u8 *data,
|
||||
u32 sr, heccr;
|
||||
int ret;
|
||||
|
||||
ret = readl_relaxed_poll_timeout(nfc->io_base + FMC2_SR,
|
||||
sr, sr & FMC2_SR_NWRF, 1,
|
||||
1000 * FMC2_TIMEOUT_MS);
|
||||
ret = regmap_read_poll_timeout(nfc->regmap, FMC2_SR, sr,
|
||||
sr & FMC2_SR_NWRF, 1,
|
||||
1000 * FMC2_TIMEOUT_MS);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "ham timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
heccr = readl_relaxed(nfc->io_base + FMC2_HECCR);
|
||||
regmap_read(nfc->regmap, FMC2_HECCR, &heccr);
|
||||
stm32_fmc2_nfc_ham_set_ecc(heccr, ecc);
|
||||
stm32_fmc2_nfc_set_ecc(nfc, false);
|
||||
|
||||
@ -603,13 +581,13 @@ static int stm32_fmc2_nfc_bch_calculate(struct nand_chip *chip, const u8 *data,
|
||||
}
|
||||
|
||||
/* Read parity bits */
|
||||
bchpbr = readl_relaxed(nfc->io_base + FMC2_BCHPBR1);
|
||||
regmap_read(nfc->regmap, FMC2_BCHPBR1, &bchpbr);
|
||||
ecc[0] = bchpbr;
|
||||
ecc[1] = bchpbr >> 8;
|
||||
ecc[2] = bchpbr >> 16;
|
||||
ecc[3] = bchpbr >> 24;
|
||||
|
||||
bchpbr = readl_relaxed(nfc->io_base + FMC2_BCHPBR2);
|
||||
regmap_read(nfc->regmap, FMC2_BCHPBR2, &bchpbr);
|
||||
ecc[4] = bchpbr;
|
||||
ecc[5] = bchpbr >> 8;
|
||||
ecc[6] = bchpbr >> 16;
|
||||
@ -617,13 +595,13 @@ static int stm32_fmc2_nfc_bch_calculate(struct nand_chip *chip, const u8 *data,
|
||||
if (chip->ecc.strength == FMC2_ECC_BCH8) {
|
||||
ecc[7] = bchpbr >> 24;
|
||||
|
||||
bchpbr = readl_relaxed(nfc->io_base + FMC2_BCHPBR3);
|
||||
regmap_read(nfc->regmap, FMC2_BCHPBR3, &bchpbr);
|
||||
ecc[8] = bchpbr;
|
||||
ecc[9] = bchpbr >> 8;
|
||||
ecc[10] = bchpbr >> 16;
|
||||
ecc[11] = bchpbr >> 24;
|
||||
|
||||
bchpbr = readl_relaxed(nfc->io_base + FMC2_BCHPBR4);
|
||||
regmap_read(nfc->regmap, FMC2_BCHPBR4, &bchpbr);
|
||||
ecc[12] = bchpbr;
|
||||
}
|
||||
|
||||
@ -685,11 +663,7 @@ static int stm32_fmc2_nfc_bch_correct(struct nand_chip *chip, u8 *dat,
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
ecc_sta[0] = readl_relaxed(nfc->io_base + FMC2_BCHDSR0);
|
||||
ecc_sta[1] = readl_relaxed(nfc->io_base + FMC2_BCHDSR1);
|
||||
ecc_sta[2] = readl_relaxed(nfc->io_base + FMC2_BCHDSR2);
|
||||
ecc_sta[3] = readl_relaxed(nfc->io_base + FMC2_BCHDSR3);
|
||||
ecc_sta[4] = readl_relaxed(nfc->io_base + FMC2_BCHDSR4);
|
||||
regmap_bulk_read(nfc->regmap, FMC2_BCHDSR0, ecc_sta, 5);
|
||||
|
||||
stm32_fmc2_nfc_set_ecc(nfc, false);
|
||||
|
||||
@ -764,30 +738,29 @@ static void stm32_fmc2_nfc_rw_page_init(struct nand_chip *chip, int page,
|
||||
{
|
||||
struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller);
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u32 csqcfgr1, csqcfgr2, csqcfgr3;
|
||||
u32 csqar1, csqar2;
|
||||
u32 ecc_offset = mtd->writesize + FMC2_BBM_LEN;
|
||||
u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR);
|
||||
/*
|
||||
* cfg[0] => csqcfgr1, cfg[1] => csqcfgr2, cfg[2] => csqcfgr3
|
||||
* cfg[3] => csqar1, cfg[4] => csqar2
|
||||
*/
|
||||
u32 cfg[5];
|
||||
|
||||
if (write_data)
|
||||
pcr |= FMC2_PCR_WEN;
|
||||
else
|
||||
pcr &= ~FMC2_PCR_WEN;
|
||||
writel_relaxed(pcr, nfc->io_base + FMC2_PCR);
|
||||
regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_WEN,
|
||||
write_data ? FMC2_PCR_WEN : 0);
|
||||
|
||||
/*
|
||||
* - Set Program Page/Page Read command
|
||||
* - Enable DMA request data
|
||||
* - Set timings
|
||||
*/
|
||||
csqcfgr1 = FMC2_CSQCFGR1_DMADEN | FMC2_CSQCFGR1_CMD1T;
|
||||
cfg[0] = FMC2_CSQCFGR1_DMADEN | FMC2_CSQCFGR1_CMD1T;
|
||||
if (write_data)
|
||||
csqcfgr1 |= FIELD_PREP(FMC2_CSQCFGR1_CMD1, NAND_CMD_SEQIN);
|
||||
cfg[0] |= FIELD_PREP(FMC2_CSQCFGR1_CMD1, NAND_CMD_SEQIN);
|
||||
else
|
||||
csqcfgr1 |= FIELD_PREP(FMC2_CSQCFGR1_CMD1, NAND_CMD_READ0) |
|
||||
FMC2_CSQCFGR1_CMD2EN |
|
||||
FIELD_PREP(FMC2_CSQCFGR1_CMD2, NAND_CMD_READSTART) |
|
||||
FMC2_CSQCFGR1_CMD2T;
|
||||
cfg[0] |= FIELD_PREP(FMC2_CSQCFGR1_CMD1, NAND_CMD_READ0) |
|
||||
FMC2_CSQCFGR1_CMD2EN |
|
||||
FIELD_PREP(FMC2_CSQCFGR1_CMD2, NAND_CMD_READSTART) |
|
||||
FMC2_CSQCFGR1_CMD2T;
|
||||
|
||||
/*
|
||||
* - Set Random Data Input/Random Data Read command
|
||||
@ -796,30 +769,29 @@ static void stm32_fmc2_nfc_rw_page_init(struct nand_chip *chip, int page,
|
||||
* - Set timings
|
||||
*/
|
||||
if (write_data)
|
||||
csqcfgr2 = FIELD_PREP(FMC2_CSQCFGR2_RCMD1, NAND_CMD_RNDIN);
|
||||
cfg[1] = FIELD_PREP(FMC2_CSQCFGR2_RCMD1, NAND_CMD_RNDIN);
|
||||
else
|
||||
csqcfgr2 = FIELD_PREP(FMC2_CSQCFGR2_RCMD1, NAND_CMD_RNDOUT) |
|
||||
FMC2_CSQCFGR2_RCMD2EN |
|
||||
FIELD_PREP(FMC2_CSQCFGR2_RCMD2,
|
||||
NAND_CMD_RNDOUTSTART) |
|
||||
FMC2_CSQCFGR2_RCMD1T |
|
||||
FMC2_CSQCFGR2_RCMD2T;
|
||||
cfg[1] = FIELD_PREP(FMC2_CSQCFGR2_RCMD1, NAND_CMD_RNDOUT) |
|
||||
FMC2_CSQCFGR2_RCMD2EN |
|
||||
FIELD_PREP(FMC2_CSQCFGR2_RCMD2, NAND_CMD_RNDOUTSTART) |
|
||||
FMC2_CSQCFGR2_RCMD1T |
|
||||
FMC2_CSQCFGR2_RCMD2T;
|
||||
if (!raw) {
|
||||
csqcfgr2 |= write_data ? 0 : FMC2_CSQCFGR2_DMASEN;
|
||||
csqcfgr2 |= FMC2_CSQCFGR2_SQSDTEN;
|
||||
cfg[1] |= write_data ? 0 : FMC2_CSQCFGR2_DMASEN;
|
||||
cfg[1] |= FMC2_CSQCFGR2_SQSDTEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* - Set the number of sectors to be written
|
||||
* - Set timings
|
||||
*/
|
||||
csqcfgr3 = FIELD_PREP(FMC2_CSQCFGR3_SNBR, chip->ecc.steps - 1);
|
||||
cfg[2] = FIELD_PREP(FMC2_CSQCFGR3_SNBR, chip->ecc.steps - 1);
|
||||
if (write_data) {
|
||||
csqcfgr3 |= FMC2_CSQCFGR3_RAC2T;
|
||||
cfg[2] |= FMC2_CSQCFGR3_RAC2T;
|
||||
if (chip->options & NAND_ROW_ADDR_3)
|
||||
csqcfgr3 |= FMC2_CSQCFGR3_AC5T;
|
||||
cfg[2] |= FMC2_CSQCFGR3_AC5T;
|
||||
else
|
||||
csqcfgr3 |= FMC2_CSQCFGR3_AC4T;
|
||||
cfg[2] |= FMC2_CSQCFGR3_AC4T;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -827,8 +799,8 @@ static void stm32_fmc2_nfc_rw_page_init(struct nand_chip *chip, int page,
|
||||
* Byte 1 and byte 2 => column, we start at 0x0
|
||||
* Byte 3 and byte 4 => page
|
||||
*/
|
||||
csqar1 = FIELD_PREP(FMC2_CSQCAR1_ADDC3, page);
|
||||
csqar1 |= FIELD_PREP(FMC2_CSQCAR1_ADDC4, page >> 8);
|
||||
cfg[3] = FIELD_PREP(FMC2_CSQCAR1_ADDC3, page);
|
||||
cfg[3] |= FIELD_PREP(FMC2_CSQCAR1_ADDC4, page >> 8);
|
||||
|
||||
/*
|
||||
* - Set chip enable number
|
||||
@ -836,23 +808,19 @@ static void stm32_fmc2_nfc_rw_page_init(struct nand_chip *chip, int page,
|
||||
* - Calculate the number of address cycles to be issued
|
||||
* - Set byte 5 of address cycle if needed
|
||||
*/
|
||||
csqar2 = FIELD_PREP(FMC2_CSQCAR2_NANDCEN, nfc->cs_sel);
|
||||
cfg[4] = FIELD_PREP(FMC2_CSQCAR2_NANDCEN, nfc->cs_sel);
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
csqar2 |= FIELD_PREP(FMC2_CSQCAR2_SAO, ecc_offset >> 1);
|
||||
cfg[4] |= FIELD_PREP(FMC2_CSQCAR2_SAO, ecc_offset >> 1);
|
||||
else
|
||||
csqar2 |= FIELD_PREP(FMC2_CSQCAR2_SAO, ecc_offset);
|
||||
cfg[4] |= FIELD_PREP(FMC2_CSQCAR2_SAO, ecc_offset);
|
||||
if (chip->options & NAND_ROW_ADDR_3) {
|
||||
csqcfgr1 |= FIELD_PREP(FMC2_CSQCFGR1_ACYNBR, 5);
|
||||
csqar2 |= FIELD_PREP(FMC2_CSQCAR2_ADDC5, page >> 16);
|
||||
cfg[0] |= FIELD_PREP(FMC2_CSQCFGR1_ACYNBR, 5);
|
||||
cfg[4] |= FIELD_PREP(FMC2_CSQCAR2_ADDC5, page >> 16);
|
||||
} else {
|
||||
csqcfgr1 |= FIELD_PREP(FMC2_CSQCFGR1_ACYNBR, 4);
|
||||
cfg[0] |= FIELD_PREP(FMC2_CSQCFGR1_ACYNBR, 4);
|
||||
}
|
||||
|
||||
writel_relaxed(csqcfgr1, nfc->io_base + FMC2_CSQCFGR1);
|
||||
writel_relaxed(csqcfgr2, nfc->io_base + FMC2_CSQCFGR2);
|
||||
writel_relaxed(csqcfgr3, nfc->io_base + FMC2_CSQCFGR3);
|
||||
writel_relaxed(csqar1, nfc->io_base + FMC2_CSQAR1);
|
||||
writel_relaxed(csqar2, nfc->io_base + FMC2_CSQAR2);
|
||||
regmap_bulk_write(nfc->regmap, FMC2_CSQCFGR1, cfg, 5);
|
||||
}
|
||||
|
||||
static void stm32_fmc2_nfc_dma_callback(void *arg)
|
||||
@ -870,7 +838,6 @@ static int stm32_fmc2_nfc_xfer(struct nand_chip *chip, const u8 *buf,
|
||||
struct dma_chan *dma_ch = nfc->dma_rx_ch;
|
||||
enum dma_data_direction dma_data_dir = DMA_FROM_DEVICE;
|
||||
enum dma_transfer_direction dma_transfer_dir = DMA_DEV_TO_MEM;
|
||||
u32 csqcr = readl_relaxed(nfc->io_base + FMC2_CSQCR);
|
||||
int eccsteps = chip->ecc.steps;
|
||||
int eccsize = chip->ecc.size;
|
||||
unsigned long timeout = msecs_to_jiffies(FMC2_TIMEOUT_MS);
|
||||
@ -948,8 +915,8 @@ static int stm32_fmc2_nfc_xfer(struct nand_chip *chip, const u8 *buf,
|
||||
stm32_fmc2_nfc_enable_seq_irq(nfc);
|
||||
|
||||
/* Start the transfer */
|
||||
csqcr |= FMC2_CSQCR_CSQSTART;
|
||||
writel_relaxed(csqcr, nfc->io_base + FMC2_CSQCR);
|
||||
regmap_update_bits(nfc->regmap, FMC2_CSQCR,
|
||||
FMC2_CSQCR_CSQSTART, FMC2_CSQCR_CSQSTART);
|
||||
|
||||
/* Wait end of sequencer transfer */
|
||||
if (!wait_for_completion_timeout(&nfc->complete, timeout)) {
|
||||
@ -1042,11 +1009,13 @@ static int stm32_fmc2_nfc_seq_write_page_raw(struct nand_chip *chip,
|
||||
}
|
||||
|
||||
/* Get a status indicating which sectors have errors */
|
||||
static inline u16 stm32_fmc2_nfc_get_mapping_status(struct stm32_fmc2_nfc *nfc)
|
||||
static u16 stm32_fmc2_nfc_get_mapping_status(struct stm32_fmc2_nfc *nfc)
|
||||
{
|
||||
u32 csqemsr = readl_relaxed(nfc->io_base + FMC2_CSQEMSR);
|
||||
u32 csqemsr;
|
||||
|
||||
return csqemsr & FMC2_CSQEMSR_SEM;
|
||||
regmap_read(nfc->regmap, FMC2_CSQEMSR, &csqemsr);
|
||||
|
||||
return FIELD_GET(FMC2_CSQEMSR_SEM, csqemsr);
|
||||
}
|
||||
|
||||
static int stm32_fmc2_nfc_seq_correct(struct nand_chip *chip, u8 *dat,
|
||||
@ -1302,22 +1271,22 @@ static int stm32_fmc2_nfc_waitrdy(struct nand_chip *chip,
|
||||
u32 isr, sr;
|
||||
|
||||
/* Check if there is no pending requests to the NAND flash */
|
||||
if (readl_relaxed_poll_timeout_atomic(nfc->io_base + FMC2_SR, sr,
|
||||
sr & FMC2_SR_NWRF, 1,
|
||||
1000 * FMC2_TIMEOUT_MS))
|
||||
if (regmap_read_poll_timeout(nfc->regmap, FMC2_SR, sr,
|
||||
sr & FMC2_SR_NWRF, 1,
|
||||
1000 * FMC2_TIMEOUT_MS))
|
||||
dev_warn(nfc->dev, "Waitrdy timeout\n");
|
||||
|
||||
/* Wait tWB before R/B# signal is low */
|
||||
timings = nand_get_sdr_timings(&chip->data_interface);
|
||||
timings = nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
ndelay(PSEC_TO_NSEC(timings->tWB_max));
|
||||
|
||||
/* R/B# signal is low, clear high level flag */
|
||||
writel_relaxed(FMC2_ICR_CIHLF, nfc->io_base + FMC2_ICR);
|
||||
regmap_write(nfc->regmap, FMC2_ICR, FMC2_ICR_CIHLF);
|
||||
|
||||
/* Wait R/B# signal is high */
|
||||
return readl_relaxed_poll_timeout_atomic(nfc->io_base + FMC2_ISR,
|
||||
isr, isr & FMC2_ISR_IHLF,
|
||||
5, 1000 * timeout_ms);
|
||||
return regmap_read_poll_timeout(nfc->regmap, FMC2_ISR, isr,
|
||||
isr & FMC2_ISR_IHLF, 5,
|
||||
1000 * FMC2_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
static int stm32_fmc2_nfc_exec_op(struct nand_chip *chip,
|
||||
@ -1375,8 +1344,9 @@ static int stm32_fmc2_nfc_exec_op(struct nand_chip *chip,
|
||||
|
||||
static void stm32_fmc2_nfc_init(struct stm32_fmc2_nfc *nfc)
|
||||
{
|
||||
u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR);
|
||||
u32 bcr1 = readl_relaxed(nfc->io_base + FMC2_BCR1);
|
||||
u32 pcr;
|
||||
|
||||
regmap_read(nfc->regmap, FMC2_PCR, &pcr);
|
||||
|
||||
/* Set CS used to undefined */
|
||||
nfc->cs_sel = -1;
|
||||
@ -1407,12 +1377,13 @@ static void stm32_fmc2_nfc_init(struct stm32_fmc2_nfc *nfc)
|
||||
pcr |= FIELD_PREP(FMC2_PCR_TAR, FMC2_PCR_TAR_DEFAULT);
|
||||
|
||||
/* Enable FMC2 controller */
|
||||
bcr1 |= FMC2_BCR1_FMC2EN;
|
||||
if (nfc->dev == nfc->cdev)
|
||||
regmap_update_bits(nfc->regmap, FMC2_BCR1,
|
||||
FMC2_BCR1_FMC2EN, FMC2_BCR1_FMC2EN);
|
||||
|
||||
writel_relaxed(bcr1, nfc->io_base + FMC2_BCR1);
|
||||
writel_relaxed(pcr, nfc->io_base + FMC2_PCR);
|
||||
writel_relaxed(FMC2_PMEM_DEFAULT, nfc->io_base + FMC2_PMEM);
|
||||
writel_relaxed(FMC2_PATT_DEFAULT, nfc->io_base + FMC2_PATT);
|
||||
regmap_write(nfc->regmap, FMC2_PCR, pcr);
|
||||
regmap_write(nfc->regmap, FMC2_PMEM, FMC2_PMEM_DEFAULT);
|
||||
regmap_write(nfc->regmap, FMC2_PATT, FMC2_PATT_DEFAULT);
|
||||
}
|
||||
|
||||
static void stm32_fmc2_nfc_calc_timings(struct nand_chip *chip,
|
||||
@ -1546,7 +1517,7 @@ static void stm32_fmc2_nfc_calc_timings(struct nand_chip *chip,
|
||||
}
|
||||
|
||||
static int stm32_fmc2_nfc_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
const struct nand_sdr_timings *sdrt;
|
||||
|
||||
@ -1570,7 +1541,7 @@ static int stm32_fmc2_nfc_dma_setup(struct stm32_fmc2_nfc *nfc)
|
||||
nfc->dma_tx_ch = dma_request_chan(nfc->dev, "tx");
|
||||
if (IS_ERR(nfc->dma_tx_ch)) {
|
||||
ret = PTR_ERR(nfc->dma_tx_ch);
|
||||
if (ret != -ENODEV)
|
||||
if (ret != -ENODEV && ret != -EPROBE_DEFER)
|
||||
dev_err(nfc->dev,
|
||||
"failed to request tx DMA channel: %d\n", ret);
|
||||
nfc->dma_tx_ch = NULL;
|
||||
@ -1580,7 +1551,7 @@ static int stm32_fmc2_nfc_dma_setup(struct stm32_fmc2_nfc *nfc)
|
||||
nfc->dma_rx_ch = dma_request_chan(nfc->dev, "rx");
|
||||
if (IS_ERR(nfc->dma_rx_ch)) {
|
||||
ret = PTR_ERR(nfc->dma_rx_ch);
|
||||
if (ret != -ENODEV)
|
||||
if (ret != -ENODEV && ret != -EPROBE_DEFER)
|
||||
dev_err(nfc->dev,
|
||||
"failed to request rx DMA channel: %d\n", ret);
|
||||
nfc->dma_rx_ch = NULL;
|
||||
@ -1590,7 +1561,7 @@ static int stm32_fmc2_nfc_dma_setup(struct stm32_fmc2_nfc *nfc)
|
||||
nfc->dma_ecc_ch = dma_request_chan(nfc->dev, "ecc");
|
||||
if (IS_ERR(nfc->dma_ecc_ch)) {
|
||||
ret = PTR_ERR(nfc->dma_ecc_ch);
|
||||
if (ret != -ENODEV)
|
||||
if (ret != -ENODEV && ret != -EPROBE_DEFER)
|
||||
dev_err(nfc->dev,
|
||||
"failed to request ecc DMA channel: %d\n", ret);
|
||||
nfc->dma_ecc_ch = NULL;
|
||||
@ -1764,7 +1735,7 @@ static int stm32_fmc2_nfc_attach_chip(struct nand_chip *chip)
|
||||
static const struct nand_controller_ops stm32_fmc2_nfc_controller_ops = {
|
||||
.attach_chip = stm32_fmc2_nfc_attach_chip,
|
||||
.exec_op = stm32_fmc2_nfc_exec_op,
|
||||
.setup_data_interface = stm32_fmc2_nfc_setup_interface,
|
||||
.setup_interface = stm32_fmc2_nfc_setup_interface,
|
||||
};
|
||||
|
||||
static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
|
||||
@ -1838,6 +1809,33 @@ static int stm32_fmc2_nfc_parse_dt(struct stm32_fmc2_nfc *nfc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_fmc2_nfc_set_cdev(struct stm32_fmc2_nfc *nfc)
|
||||
{
|
||||
struct device *dev = nfc->dev;
|
||||
bool ebi_found = false;
|
||||
|
||||
if (dev->parent && of_device_is_compatible(dev->parent->of_node,
|
||||
"st,stm32mp1-fmc2-ebi"))
|
||||
ebi_found = true;
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "st,stm32mp1-fmc2-nfc")) {
|
||||
if (ebi_found) {
|
||||
nfc->cdev = dev->parent;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ebi_found)
|
||||
return -EINVAL;
|
||||
|
||||
nfc->cdev = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -1847,7 +1845,9 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *chip;
|
||||
struct resource cres;
|
||||
int chip_cs, mem_region, ret, irq;
|
||||
int start_region = 0;
|
||||
|
||||
nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
|
||||
if (!nfc)
|
||||
@ -1857,18 +1857,28 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
|
||||
nand_controller_init(&nfc->base);
|
||||
nfc->base.ops = &stm32_fmc2_nfc_controller_ops;
|
||||
|
||||
ret = stm32_fmc2_nfc_set_cdev(nfc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm32_fmc2_nfc_parse_dt(nfc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
nfc->io_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(nfc->io_base))
|
||||
return PTR_ERR(nfc->io_base);
|
||||
ret = of_address_to_resource(nfc->cdev->of_node, 0, &cres);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nfc->io_phys_addr = res->start;
|
||||
nfc->io_phys_addr = cres.start;
|
||||
|
||||
for (chip_cs = 0, mem_region = 1; chip_cs < FMC2_MAX_CE;
|
||||
nfc->regmap = device_node_to_regmap(nfc->cdev->of_node);
|
||||
if (IS_ERR(nfc->regmap))
|
||||
return PTR_ERR(nfc->regmap);
|
||||
|
||||
if (nfc->dev == nfc->cdev)
|
||||
start_region = 1;
|
||||
|
||||
for (chip_cs = 0, mem_region = start_region; chip_cs < FMC2_MAX_CE;
|
||||
chip_cs++, mem_region += 3) {
|
||||
if (!(nfc->cs_assigned & BIT(chip_cs)))
|
||||
continue;
|
||||
@ -1906,7 +1916,7 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
|
||||
|
||||
init_completion(&nfc->complete);
|
||||
|
||||
nfc->clk = devm_clk_get(dev, NULL);
|
||||
nfc->clk = devm_clk_get(nfc->cdev, NULL);
|
||||
if (IS_ERR(nfc->clk))
|
||||
return PTR_ERR(nfc->clk);
|
||||
|
||||
@ -2047,6 +2057,7 @@ static SIMPLE_DEV_PM_OPS(stm32_fmc2_nfc_pm_ops, stm32_fmc2_nfc_suspend,
|
||||
|
||||
static const struct of_device_id stm32_fmc2_nfc_match[] = {
|
||||
{.compatible = "st,stm32mp15-fmc2"},
|
||||
{.compatible = "st,stm32mp1-fmc2-nfc"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_fmc2_nfc_match);
|
||||
|
@ -1376,8 +1376,8 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
|
||||
#define sunxi_nand_lookup_timing(l, p, c) \
|
||||
_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
|
||||
|
||||
static int sunxi_nfc_setup_data_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
|
||||
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
|
||||
@ -1920,7 +1920,7 @@ static int sunxi_nfc_exec_op(struct nand_chip *nand,
|
||||
|
||||
static const struct nand_controller_ops sunxi_nand_controller_ops = {
|
||||
.attach_chip = sunxi_nand_attach_chip,
|
||||
.setup_data_interface = sunxi_nfc_setup_data_interface,
|
||||
.setup_interface = sunxi_nfc_setup_interface,
|
||||
.exec_op = sunxi_nfc_exec_op,
|
||||
};
|
||||
|
||||
|
@ -113,53 +113,11 @@ struct tango_chip {
|
||||
|
||||
#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3))
|
||||
|
||||
static void tango_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl)
|
||||
{
|
||||
struct tango_chip *tchip = to_tango_chip(chip);
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
writeb_relaxed(dat, tchip->base + PBUS_CMD);
|
||||
|
||||
if (ctrl & NAND_ALE)
|
||||
writeb_relaxed(dat, tchip->base + PBUS_ADDR);
|
||||
}
|
||||
|
||||
static int tango_dev_ready(struct nand_chip *chip)
|
||||
{
|
||||
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||
|
||||
return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY;
|
||||
}
|
||||
|
||||
static u8 tango_read_byte(struct nand_chip *chip)
|
||||
{
|
||||
struct tango_chip *tchip = to_tango_chip(chip);
|
||||
|
||||
return readb_relaxed(tchip->base + PBUS_DATA);
|
||||
}
|
||||
|
||||
static void tango_read_buf(struct nand_chip *chip, u8 *buf, int len)
|
||||
{
|
||||
struct tango_chip *tchip = to_tango_chip(chip);
|
||||
|
||||
ioread8_rep(tchip->base + PBUS_DATA, buf, len);
|
||||
}
|
||||
|
||||
static void tango_write_buf(struct nand_chip *chip, const u8 *buf, int len)
|
||||
{
|
||||
struct tango_chip *tchip = to_tango_chip(chip);
|
||||
|
||||
iowrite8_rep(tchip->base + PBUS_DATA, buf, len);
|
||||
}
|
||||
|
||||
static void tango_select_chip(struct nand_chip *chip, int idx)
|
||||
static void tango_select_target(struct nand_chip *chip, unsigned int cs)
|
||||
{
|
||||
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||
struct tango_chip *tchip = to_tango_chip(chip);
|
||||
|
||||
if (idx < 0)
|
||||
return; /* No "chip unselect" function */
|
||||
|
||||
writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1);
|
||||
writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2);
|
||||
writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG);
|
||||
@ -168,6 +126,69 @@ static void tango_select_chip(struct nand_chip *chip, int idx)
|
||||
writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG);
|
||||
}
|
||||
|
||||
static int tango_waitrdy(struct nand_chip *chip, unsigned int timeout_ms)
|
||||
{
|
||||
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||
u32 status;
|
||||
|
||||
return readl_relaxed_poll_timeout(nfc->pbus_base + PBUS_CS_CTRL,
|
||||
status, status & PBUS_IORDY, 20,
|
||||
timeout_ms);
|
||||
}
|
||||
|
||||
static int tango_exec_instr(struct nand_chip *chip,
|
||||
const struct nand_op_instr *instr)
|
||||
{
|
||||
struct tango_chip *tchip = to_tango_chip(chip);
|
||||
unsigned int i;
|
||||
|
||||
switch (instr->type) {
|
||||
case NAND_OP_CMD_INSTR:
|
||||
writeb_relaxed(instr->ctx.cmd.opcode, tchip->base + PBUS_CMD);
|
||||
return 0;
|
||||
case NAND_OP_ADDR_INSTR:
|
||||
for (i = 0; i < instr->ctx.addr.naddrs; i++)
|
||||
writeb_relaxed(instr->ctx.addr.addrs[i],
|
||||
tchip->base + PBUS_ADDR);
|
||||
return 0;
|
||||
case NAND_OP_DATA_IN_INSTR:
|
||||
ioread8_rep(tchip->base + PBUS_DATA, instr->ctx.data.buf.in,
|
||||
instr->ctx.data.len);
|
||||
return 0;
|
||||
case NAND_OP_DATA_OUT_INSTR:
|
||||
iowrite8_rep(tchip->base + PBUS_DATA, instr->ctx.data.buf.out,
|
||||
instr->ctx.data.len);
|
||||
return 0;
|
||||
case NAND_OP_WAITRDY_INSTR:
|
||||
return tango_waitrdy(chip,
|
||||
instr->ctx.waitrdy.timeout_ms);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tango_exec_op(struct nand_chip *chip,
|
||||
const struct nand_operation *op,
|
||||
bool check_only)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
|
||||
if (check_only)
|
||||
return 0;
|
||||
|
||||
tango_select_target(chip, op->cs);
|
||||
for (i = 0; i < op->ninstrs; i++) {
|
||||
ret = tango_exec_instr(chip, &op->instrs[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The controller does not check for bitflips in erased pages,
|
||||
* therefore software must check instead.
|
||||
@ -279,6 +300,7 @@ static int tango_read_page(struct nand_chip *chip, u8 *buf,
|
||||
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||
int err, res, len = mtd->writesize;
|
||||
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
if (oob_required)
|
||||
chip->ecc.read_oob(chip, page);
|
||||
|
||||
@ -300,22 +322,30 @@ static int tango_write_page(struct nand_chip *chip, const u8 *buf,
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||
int err, status, len = mtd->writesize;
|
||||
const struct nand_sdr_timings *timings;
|
||||
int err, len = mtd->writesize;
|
||||
u8 status;
|
||||
|
||||
/* Calling tango_write_oob() would send PAGEPROG twice */
|
||||
if (oob_required)
|
||||
return -ENOTSUPP;
|
||||
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
writel_relaxed(0xffffffff, nfc->mem_base + METADATA);
|
||||
err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
status = chip->legacy.waitfunc(chip);
|
||||
if (status & NAND_STATUS_FAIL)
|
||||
return -EIO;
|
||||
timings = nand_get_sdr_timings(nand_get_interface_config(chip));
|
||||
err = tango_waitrdy(chip, PSEC_TO_MSEC(timings->tR_max));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
err = nand_status_op(chip, &status);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return (status & NAND_STATUS_FAIL) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)
|
||||
@ -326,7 +356,9 @@ static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)
|
||||
/* skip over "len" bytes */
|
||||
nand_change_read_column_op(chip, *pos, NULL, 0, false);
|
||||
} else {
|
||||
tango_read_buf(chip, *buf, len);
|
||||
struct tango_chip *tchip = to_tango_chip(chip);
|
||||
|
||||
ioread8_rep(tchip->base + PBUS_DATA, *buf, len);
|
||||
*buf += len;
|
||||
}
|
||||
}
|
||||
@ -339,7 +371,9 @@ static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos)
|
||||
/* skip over "len" bytes */
|
||||
nand_change_write_column_op(chip, *pos, NULL, 0, false);
|
||||
} else {
|
||||
tango_write_buf(chip, *buf, len);
|
||||
struct tango_chip *tchip = to_tango_chip(chip);
|
||||
|
||||
iowrite8_rep(tchip->base + PBUS_DATA, *buf, len);
|
||||
*buf += len;
|
||||
}
|
||||
}
|
||||
@ -420,6 +454,7 @@ static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob)
|
||||
static int tango_read_page_raw(struct nand_chip *chip, u8 *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
raw_read(chip, buf, chip->oob_poi);
|
||||
return 0;
|
||||
@ -428,6 +463,7 @@ static int tango_read_page_raw(struct nand_chip *chip, u8 *buf,
|
||||
static int tango_write_page_raw(struct nand_chip *chip, const u8 *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
raw_write(chip, buf, chip->oob_poi);
|
||||
return nand_prog_page_end_op(chip);
|
||||
@ -435,6 +471,7 @@ static int tango_write_page_raw(struct nand_chip *chip, const u8 *buf,
|
||||
|
||||
static int tango_read_oob(struct nand_chip *chip, int page)
|
||||
{
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
raw_read(chip, NULL, chip->oob_poi);
|
||||
return 0;
|
||||
@ -442,6 +479,7 @@ static int tango_read_oob(struct nand_chip *chip, int page)
|
||||
|
||||
static int tango_write_oob(struct nand_chip *chip, int page)
|
||||
{
|
||||
tango_select_target(chip, chip->cur_cs);
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
raw_write(chip, NULL, chip->oob_poi);
|
||||
return nand_prog_page_end_op(chip);
|
||||
@ -477,7 +515,7 @@ static u32 to_ticks(int kHz, int ps)
|
||||
}
|
||||
|
||||
static int tango_set_timings(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
|
||||
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
|
||||
@ -527,7 +565,8 @@ static int tango_attach_chip(struct nand_chip *chip)
|
||||
|
||||
static const struct nand_controller_ops tango_controller_ops = {
|
||||
.attach_chip = tango_attach_chip,
|
||||
.setup_data_interface = tango_set_timings,
|
||||
.setup_interface = tango_set_timings,
|
||||
.exec_op = tango_exec_op,
|
||||
};
|
||||
|
||||
static int chip_init(struct device *dev, struct device_node *np)
|
||||
@ -562,12 +601,6 @@ static int chip_init(struct device *dev, struct device_node *np)
|
||||
ecc = &chip->ecc;
|
||||
mtd = nand_to_mtd(chip);
|
||||
|
||||
chip->legacy.read_byte = tango_read_byte;
|
||||
chip->legacy.write_buf = tango_write_buf;
|
||||
chip->legacy.read_buf = tango_read_buf;
|
||||
chip->legacy.select_chip = tango_select_chip;
|
||||
chip->legacy.cmd_ctrl = tango_cmd_ctrl;
|
||||
chip->legacy.dev_ready = tango_dev_ready;
|
||||
chip->options = NAND_USES_DMA |
|
||||
NAND_NO_SUBPAGE_WRITE |
|
||||
NAND_WAIT_TCCS;
|
||||
|
@ -813,8 +813,8 @@ static void tegra_nand_setup_timing(struct tegra_nand_controller *ctrl,
|
||||
writel_relaxed(reg, ctrl->regs + TIMING_2);
|
||||
}
|
||||
|
||||
static int tegra_nand_setup_data_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_data_interface *conf)
|
||||
static int tegra_nand_setup_interface(struct nand_chip *chip, int csline,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
|
||||
const struct nand_sdr_timings *timings;
|
||||
@ -1053,7 +1053,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip)
|
||||
static const struct nand_controller_ops tegra_nand_controller_ops = {
|
||||
.attach_chip = &tegra_nand_attach_chip,
|
||||
.exec_op = tegra_nand_exec_op,
|
||||
.setup_data_interface = tegra_nand_setup_data_interface,
|
||||
.setup_interface = tegra_nand_setup_interface,
|
||||
};
|
||||
|
||||
static int tegra_nand_chips_init(struct device *dev,
|
||||
|
@ -22,6 +22,11 @@
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#ifdef CONFIG_MIPS
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/fw/cfe/cfe_api.h>
|
||||
#endif /* CONFIG_MIPS */
|
||||
|
||||
#define BCM963XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */
|
||||
|
||||
#define BCM963XX_CFE_MAGIC_OFFSET 0x4e0
|
||||
@ -32,28 +37,15 @@
|
||||
#define STR_NULL_TERMINATE(x) \
|
||||
do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0)
|
||||
|
||||
static int bcm63xx_detect_cfe(struct mtd_info *master)
|
||||
static inline int bcm63xx_detect_cfe(void)
|
||||
{
|
||||
char buf[9];
|
||||
int ret;
|
||||
size_t retlen;
|
||||
int ret = 0;
|
||||
|
||||
ret = mtd_read(master, BCM963XX_CFE_VERSION_OFFSET, 5, &retlen,
|
||||
(void *)buf);
|
||||
buf[retlen] = 0;
|
||||
#ifdef CONFIG_MIPS
|
||||
ret = (fw_arg3 == CFE_EPTSEAL);
|
||||
#endif /* CONFIG_MIPS */
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (strncmp("cfe-v", buf, 5) == 0)
|
||||
return 0;
|
||||
|
||||
/* very old CFE's do not have the cfe-v string, so check for magic */
|
||||
ret = mtd_read(master, BCM963XX_CFE_MAGIC_OFFSET, 8, &retlen,
|
||||
(void *)buf);
|
||||
buf[retlen] = 0;
|
||||
|
||||
return strncmp("CFE1CFE1", buf, 8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcm63xx_read_nvram(struct mtd_info *master,
|
||||
@ -138,7 +130,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
||||
struct bcm963xx_nvram *nvram = NULL;
|
||||
int ret;
|
||||
|
||||
if (bcm63xx_detect_cfe(master))
|
||||
if (!bcm63xx_detect_cfe())
|
||||
return -EINVAL;
|
||||
|
||||
nvram = vzalloc(sizeof(*nvram));
|
||||
|
@ -68,7 +68,9 @@ static const struct pci_device_id intel_spi_pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x06a4), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x1bca), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info },
|
||||
|
@ -292,7 +292,7 @@ static int intel_spi_wait_hw_busy(struct intel_spi *ispi)
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout(ispi->base + HSFSTS_CTL, val,
|
||||
!(val & HSFSTS_CTL_SCIP), 40,
|
||||
!(val & HSFSTS_CTL_SCIP), 0,
|
||||
INTEL_SPI_TIMEOUT * 1000);
|
||||
}
|
||||
|
||||
@ -301,7 +301,7 @@ static int intel_spi_wait_sw_busy(struct intel_spi *ispi)
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val,
|
||||
!(val & SSFSTS_CTL_SCIP), 40,
|
||||
!(val & SSFSTS_CTL_SCIP), 0,
|
||||
INTEL_SPI_TIMEOUT * 1000);
|
||||
}
|
||||
|
||||
@ -612,6 +612,15 @@ static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We hope that HW sequencer will do the right thing automatically and
|
||||
* with the SW sequencer we cannot use preopcode anyway, so just ignore
|
||||
* the Write Disable operation and pretend it was completed
|
||||
* successfully.
|
||||
*/
|
||||
if (opcode == SPINOR_OP_WRDI)
|
||||
return 0;
|
||||
|
||||
writel(0, ispi->base + FADDR);
|
||||
|
||||
/* Write the value beforehand */
|
||||
|
@ -1907,15 +1907,16 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_sr1_bit6_quad_enable() - Set the Quad Enable BIT(6) in the Status
|
||||
* Register 1.
|
||||
* spi_nor_sr1_bit6_quad_enable() - Set/Unset the Quad Enable BIT(6) in the
|
||||
* Status Register 1.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @enable: true to enable Quad mode, false to disable Quad mode.
|
||||
*
|
||||
* Bit 6 of the Status Register 1 is the QE bit for Macronix like QSPI memories.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor)
|
||||
int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -1923,45 +1924,56 @@ int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6)
|
||||
if ((enable && (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6)) ||
|
||||
(!enable && !(nor->bouncebuf[0] & SR1_QUAD_EN_BIT6)))
|
||||
return 0;
|
||||
|
||||
nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6;
|
||||
if (enable)
|
||||
nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6;
|
||||
else
|
||||
nor->bouncebuf[0] &= ~SR1_QUAD_EN_BIT6;
|
||||
|
||||
return spi_nor_write_sr1_and_check(nor, nor->bouncebuf[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_sr2_bit1_quad_enable() - set the Quad Enable BIT(1) in the Status
|
||||
* Register 2.
|
||||
* spi_nor_sr2_bit1_quad_enable() - set/unset the Quad Enable BIT(1) in the
|
||||
* Status Register 2.
|
||||
* @nor: pointer to a 'struct spi_nor'.
|
||||
* @enable: true to enable Quad mode, false to disable Quad mode.
|
||||
*
|
||||
* Bit 1 of the Status Register 2 is the QE bit for Spansion like QSPI memories.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor)
|
||||
int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (nor->flags & SNOR_F_NO_READ_CR)
|
||||
return spi_nor_write_16bit_cr_and_check(nor, SR2_QUAD_EN_BIT1);
|
||||
return spi_nor_write_16bit_cr_and_check(nor,
|
||||
enable ? SR2_QUAD_EN_BIT1 : 0);
|
||||
|
||||
ret = spi_nor_read_cr(nor, nor->bouncebuf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1)
|
||||
if ((enable && (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1)) ||
|
||||
(!enable && !(nor->bouncebuf[0] & SR2_QUAD_EN_BIT1)))
|
||||
return 0;
|
||||
|
||||
nor->bouncebuf[0] |= SR2_QUAD_EN_BIT1;
|
||||
if (enable)
|
||||
nor->bouncebuf[0] |= SR2_QUAD_EN_BIT1;
|
||||
else
|
||||
nor->bouncebuf[0] &= ~SR2_QUAD_EN_BIT1;
|
||||
|
||||
return spi_nor_write_16bit_cr_and_check(nor, nor->bouncebuf[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_sr2_bit7_quad_enable() - set QE bit in Status Register 2.
|
||||
* spi_nor_sr2_bit7_quad_enable() - set/unset QE bit in Status Register 2.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @enable: true to enable Quad mode, false to disable Quad mode.
|
||||
*
|
||||
* Set the Quad Enable (QE) bit in the Status Register 2.
|
||||
*
|
||||
@ -1971,7 +1983,7 @@ int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor)
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor)
|
||||
int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor, bool enable)
|
||||
{
|
||||
u8 *sr2 = nor->bouncebuf;
|
||||
int ret;
|
||||
@ -1981,11 +1993,15 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor)
|
||||
ret = spi_nor_read_sr2(nor, sr2);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (*sr2 & SR2_QUAD_EN_BIT7)
|
||||
if ((enable && (*sr2 & SR2_QUAD_EN_BIT7)) ||
|
||||
(!enable && !(*sr2 & SR2_QUAD_EN_BIT7)))
|
||||
return 0;
|
||||
|
||||
/* Update the Quad Enable bit. */
|
||||
*sr2 |= SR2_QUAD_EN_BIT7;
|
||||
if (enable)
|
||||
*sr2 |= SR2_QUAD_EN_BIT7;
|
||||
else
|
||||
*sr2 &= ~SR2_QUAD_EN_BIT7;
|
||||
|
||||
ret = spi_nor_write_sr2(nor, sr2);
|
||||
if (ret)
|
||||
@ -2898,12 +2914,13 @@ static int spi_nor_init_params(struct spi_nor *nor)
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_quad_enable() - enable Quad I/O if needed.
|
||||
* spi_nor_quad_enable() - enable/disable Quad I/O if needed.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @enable: true to enable Quad mode. false to disable Quad mode.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
static int spi_nor_quad_enable(struct spi_nor *nor)
|
||||
static int spi_nor_quad_enable(struct spi_nor *nor, bool enable)
|
||||
{
|
||||
if (!nor->params->quad_enable)
|
||||
return 0;
|
||||
@ -2912,7 +2929,7 @@ static int spi_nor_quad_enable(struct spi_nor *nor)
|
||||
spi_nor_get_protocol_width(nor->write_proto) == 4))
|
||||
return 0;
|
||||
|
||||
return nor->params->quad_enable(nor);
|
||||
return nor->params->quad_enable(nor, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2936,7 +2953,7 @@ static int spi_nor_init(struct spi_nor *nor)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = spi_nor_quad_enable(nor);
|
||||
err = spi_nor_quad_enable(nor, true);
|
||||
if (err) {
|
||||
dev_dbg(nor->dev, "quad mode not supported\n");
|
||||
return err;
|
||||
@ -2983,6 +3000,8 @@ void spi_nor_restore(struct spi_nor *nor)
|
||||
if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
|
||||
nor->flags & SNOR_F_BROKEN_RESET)
|
||||
nor->params->set_4byte_addr_mode(nor, false);
|
||||
|
||||
spi_nor_quad_enable(nor, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_nor_restore);
|
||||
|
||||
|
@ -198,7 +198,7 @@ struct spi_nor_locking_ops {
|
||||
* higher index in the array, the higher priority.
|
||||
* @erase_map: the erase map parsed from the SFDP Sector Map Parameter
|
||||
* Table.
|
||||
* @quad_enable: enables SPI NOR quad mode.
|
||||
* @quad_enable: enables/disables SPI NOR Quad mode.
|
||||
* @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode.
|
||||
* @convert_addr: converts an absolute address into something the flash
|
||||
* will understand. Particularly useful when pagesize is
|
||||
@ -219,7 +219,7 @@ struct spi_nor_flash_parameter {
|
||||
|
||||
struct spi_nor_erase_map erase_map;
|
||||
|
||||
int (*quad_enable)(struct spi_nor *nor);
|
||||
int (*quad_enable)(struct spi_nor *nor, bool enable);
|
||||
int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable);
|
||||
u32 (*convert_addr)(struct spi_nor *nor, u32 addr);
|
||||
int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps);
|
||||
@ -406,9 +406,9 @@ int spi_nor_write_ear(struct spi_nor *nor, u8 ear);
|
||||
int spi_nor_wait_till_ready(struct spi_nor *nor);
|
||||
int spi_nor_lock_and_prep(struct spi_nor *nor);
|
||||
void spi_nor_unlock_and_unprep(struct spi_nor *nor);
|
||||
int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor);
|
||||
int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor);
|
||||
int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor);
|
||||
int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor, bool enable);
|
||||
int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor, bool enable);
|
||||
int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor, bool enable);
|
||||
|
||||
int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr);
|
||||
ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
|
||||
|
@ -52,6 +52,9 @@ static const struct flash_info macronix_parts[] = {
|
||||
{ "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25r1635f", INFO(0xc22815, 0, 64 * 1024, 32,
|
||||
SECT_4K | SPI_NOR_DUAL_READ |
|
||||
SPI_NOR_QUAD_READ) },
|
||||
{ "mx25r3235f", INFO(0xc22816, 0, 64 * 1024, 64,
|
||||
SECT_4K | SPI_NOR_DUAL_READ |
|
||||
SPI_NOR_QUAD_READ) },
|
||||
@ -84,6 +87,9 @@ static const struct flash_info macronix_parts[] = {
|
||||
SPI_NOR_QUAD_READ) },
|
||||
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048,
|
||||
SPI_NOR_QUAD_READ) },
|
||||
{ "mx66u2g45g", INFO(0xc2253c, 0, 64 * 1024, 4096,
|
||||
SECT_4K | SPI_NOR_DUAL_READ |
|
||||
SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
};
|
||||
|
||||
static void macronix_default_init(struct spi_nor *nor)
|
||||
|
@ -71,8 +71,8 @@ static const struct flash_info st_parts[] = {
|
||||
SECT_4K | USE_FSR | SPI_NOR_QUAD_READ |
|
||||
NO_CHIP_ERASE) },
|
||||
{ "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096,
|
||||
SECT_4K | USE_FSR | SPI_NOR_QUAD_READ |
|
||||
NO_CHIP_ERASE) },
|
||||
SECT_4K | USE_FSR | SPI_NOR_DUAL_READ |
|
||||
SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
|
||||
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
|
||||
{ "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) },
|
||||
|
@ -598,7 +598,8 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
dev_dbg(nor->dev, "BFPT QER reserved value used\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stop here if not JESD216 rev C or later. */
|
||||
|
@ -64,7 +64,6 @@ static const struct flash_info spansion_parts[] = {
|
||||
{ "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256,
|
||||
SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR)
|
||||
.fixups = &s25fs_s_fixups, },
|
||||
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
|
||||
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
|
||||
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
|
||||
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64,
|
||||
@ -84,7 +83,8 @@ static const struct flash_info spansion_parts[] = {
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32,
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128,
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "s25fl116k", INFO(0x014015, 0, 64 * 1024, 32,
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
|
@ -64,10 +64,12 @@ static const struct flash_info winbond_parts[] = {
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
|
||||
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
|
||||
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128,
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128,
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
|
||||
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
|
||||
{ "w25q64jvm", INFO(0xef7017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256,
|
||||
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
|
||||
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MTD_HYPERBUS_H__
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
struct nand_device;
|
||||
|
||||
/**
|
||||
* struct nand_memory_organization - Memory organization structure
|
||||
* @bits_per_cell: number of bits per NAND cell
|
||||
@ -114,11 +116,11 @@ struct nand_page_io_req {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_ecc_req - NAND ECC requirements
|
||||
* struct nand_ecc_props - NAND ECC properties
|
||||
* @strength: ECC strength
|
||||
* @step_size: ECC step/block size
|
||||
* @step_size: Number of bytes per step
|
||||
*/
|
||||
struct nand_ecc_req {
|
||||
struct nand_ecc_props {
|
||||
unsigned int strength;
|
||||
unsigned int step_size;
|
||||
};
|
||||
@ -133,8 +135,6 @@ struct nand_bbt {
|
||||
unsigned long *cache;
|
||||
};
|
||||
|
||||
struct nand_device;
|
||||
|
||||
/**
|
||||
* struct nand_ops - NAND operations
|
||||
* @erase: erase a specific block. No need to check if the block is bad before
|
||||
@ -179,7 +179,7 @@ struct nand_ops {
|
||||
struct nand_device {
|
||||
struct mtd_info mtd;
|
||||
struct nand_memory_organization memorg;
|
||||
struct nand_ecc_req eccreq;
|
||||
struct nand_ecc_props eccreq;
|
||||
struct nand_row_converter rowconv;
|
||||
struct nand_bbt bbt;
|
||||
const struct nand_ops *ops;
|
||||
|
@ -19,7 +19,7 @@
|
||||
/* Identification info for LPDDR chip */
|
||||
#define PFOW_MANUFACTURER_ID 0x0020
|
||||
#define PFOW_DEVICE_ID 0x0022
|
||||
/* Address in PFOW where prog buffer can can be found */
|
||||
/* Address in PFOW where prog buffer can be found */
|
||||
#define PFOW_PROGRAM_BUFFER_OFFSET 0x0040
|
||||
/* Size of program buffer in words */
|
||||
#define PFOW_PROGRAM_BUFFER_SIZE 0x0042
|
||||
|
@ -492,22 +492,22 @@ struct nand_sdr_timings {
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nand_data_interface_type - NAND interface timing type
|
||||
* enum nand_interface_type - NAND interface type
|
||||
* @NAND_SDR_IFACE: Single Data Rate interface
|
||||
*/
|
||||
enum nand_data_interface_type {
|
||||
enum nand_interface_type {
|
||||
NAND_SDR_IFACE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_data_interface - NAND interface timing
|
||||
* struct nand_interface_config - NAND interface timing
|
||||
* @type: type of the timing
|
||||
* @timings: The timing information
|
||||
* @timings.mode: Timing mode as defined in the specification
|
||||
* @timings.sdr: Use it when @type is %NAND_SDR_IFACE.
|
||||
*/
|
||||
struct nand_data_interface {
|
||||
enum nand_data_interface_type type;
|
||||
struct nand_interface_config {
|
||||
enum nand_interface_type type;
|
||||
struct nand_timings {
|
||||
unsigned int mode;
|
||||
union {
|
||||
@ -521,7 +521,7 @@ struct nand_data_interface {
|
||||
* @conf: The data interface
|
||||
*/
|
||||
static inline const struct nand_sdr_timings *
|
||||
nand_get_sdr_timings(const struct nand_data_interface *conf)
|
||||
nand_get_sdr_timings(const struct nand_interface_config *conf)
|
||||
{
|
||||
if (conf->type != NAND_SDR_IFACE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
@ -944,11 +944,10 @@ static inline void nand_op_trace(const char *prefix,
|
||||
* This method replaces chip->legacy.cmdfunc(),
|
||||
* chip->legacy.{read,write}_{buf,byte,word}(),
|
||||
* chip->legacy.dev_ready() and chip->legacy.waifunc().
|
||||
* @setup_data_interface: setup the data interface and timing. If
|
||||
* chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this
|
||||
* means the configuration should not be applied but
|
||||
* only checked.
|
||||
* This hook is optional.
|
||||
* @setup_interface: setup the data interface and timing. If chipnr is set to
|
||||
* %NAND_DATA_IFACE_CHECK_ONLY this means the configuration
|
||||
* should not be applied but only checked.
|
||||
* This hook is optional.
|
||||
*/
|
||||
struct nand_controller_ops {
|
||||
int (*attach_chip)(struct nand_chip *chip);
|
||||
@ -956,8 +955,8 @@ struct nand_controller_ops {
|
||||
int (*exec_op)(struct nand_chip *chip,
|
||||
const struct nand_operation *op,
|
||||
bool check_only);
|
||||
int (*setup_data_interface)(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf);
|
||||
int (*setup_interface)(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1028,140 +1027,138 @@ struct nand_legacy {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_chip - NAND Private Flash Chip Data
|
||||
* @base: Inherit from the generic NAND device
|
||||
* @legacy: All legacy fields/hooks. If you develop a new driver,
|
||||
* don't even try to use any of these fields/hooks, and if
|
||||
* you're modifying an existing driver that is using those
|
||||
* fields/hooks, you should consider reworking the driver
|
||||
* avoid using them.
|
||||
* @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for
|
||||
* setting the read-retry mode. Mostly needed for MLC NAND.
|
||||
* @ecc: [BOARDSPECIFIC] ECC control structure
|
||||
* @buf_align: minimum buffer alignment required by a platform
|
||||
* @oob_poi: "poison value buffer," used for laying out OOB data
|
||||
* before writing
|
||||
* @page_shift: [INTERN] number of address bits in a page (column
|
||||
* address bits).
|
||||
* @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
|
||||
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
|
||||
* @chip_shift: [INTERN] number of address bits in one chip
|
||||
* @options: [BOARDSPECIFIC] various chip options. They can partly
|
||||
* be set to inform nand_scan about special functionality.
|
||||
* See the defines for further explanation.
|
||||
* @bbt_options: [INTERN] bad block specific options. All options used
|
||||
* here must come from bbm.h. By default, these options
|
||||
* will be copied to the appropriate nand_bbt_descr's.
|
||||
* @badblockpos: [INTERN] position of the bad block marker in the oob
|
||||
* area.
|
||||
* @badblockbits: [INTERN] minimum number of set bits in a good block's
|
||||
* bad block marker position; i.e., BBM == 11110111b is
|
||||
* not bad when badblockbits == 7
|
||||
* @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
|
||||
* set to the actually used ONFI mode if the chip is
|
||||
* ONFI compliant or deduced from the datasheet if
|
||||
* the NAND chip is not ONFI compliant.
|
||||
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
||||
* @data_buf: [INTERN] buffer for data, size is (page size + oobsize).
|
||||
* @pagecache: Structure containing page cache related fields
|
||||
* @pagecache.bitflips: Number of bitflips of the cached page
|
||||
* @pagecache.page: Page number currently in the cache. -1 means no page is
|
||||
* currently cached
|
||||
* @subpagesize: [INTERN] holds the subpagesize
|
||||
* @id: [INTERN] holds NAND ID
|
||||
* @parameters: [INTERN] holds generic parameters under an easily
|
||||
* readable form.
|
||||
* @data_interface: [INTERN] NAND interface timing information
|
||||
* @cur_cs: currently selected target. -1 means no target selected,
|
||||
* otherwise we should always have cur_cs >= 0 &&
|
||||
* cur_cs < nanddev_ntargets(). NAND Controller drivers
|
||||
* should not modify this value, but they're allowed to
|
||||
* read it.
|
||||
* @read_retries: [INTERN] the number of read retry modes supported
|
||||
* @lock: lock protecting the suspended field. Also used to
|
||||
* serialize accesses to the NAND device.
|
||||
* @suspended: set to 1 when the device is suspended, 0 when it's not.
|
||||
* @suspend: [REPLACEABLE] specific NAND device suspend operation
|
||||
* @resume: [REPLACEABLE] specific NAND device resume operation
|
||||
* @bbt: [INTERN] bad block table pointer
|
||||
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
|
||||
* lookup.
|
||||
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor
|
||||
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial
|
||||
* bad block scan.
|
||||
* @controller: [REPLACEABLE] a pointer to a hardware controller
|
||||
* structure which is shared among multiple independent
|
||||
* devices.
|
||||
* @priv: [OPTIONAL] pointer to private chip data
|
||||
* @manufacturer: [INTERN] Contains manufacturer information
|
||||
* @manufacturer.desc: [INTERN] Contains manufacturer's description
|
||||
* @manufacturer.priv: [INTERN] Contains manufacturer private information
|
||||
* @lock_area: [REPLACEABLE] specific NAND chip lock operation
|
||||
* @unlock_area: [REPLACEABLE] specific NAND chip unlock operation
|
||||
* struct nand_chip_ops - NAND chip operations
|
||||
* @suspend: Suspend operation
|
||||
* @resume: Resume operation
|
||||
* @lock_area: Lock operation
|
||||
* @unlock_area: Unlock operation
|
||||
* @setup_read_retry: Set the read-retry mode (mostly needed for MLC NANDs)
|
||||
* @choose_interface_config: Choose the best interface configuration
|
||||
*/
|
||||
struct nand_chip_ops {
|
||||
int (*suspend)(struct nand_chip *chip);
|
||||
void (*resume)(struct nand_chip *chip);
|
||||
int (*lock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
|
||||
int (*unlock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
|
||||
int (*setup_read_retry)(struct nand_chip *chip, int retry_mode);
|
||||
int (*choose_interface_config)(struct nand_chip *chip,
|
||||
struct nand_interface_config *iface);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_manufacturer - NAND manufacturer structure
|
||||
* @desc: The manufacturer description
|
||||
* @priv: Private information for the manufacturer driver
|
||||
*/
|
||||
struct nand_manufacturer {
|
||||
const struct nand_manufacturer_desc *desc;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_chip - NAND Private Flash Chip Data
|
||||
* @base: Inherit from the generic NAND device
|
||||
* @id: Holds NAND ID
|
||||
* @parameters: Holds generic parameters under an easily readable form
|
||||
* @manufacturer: Manufacturer information
|
||||
* @ops: NAND chip operations
|
||||
* @legacy: All legacy fields/hooks. If you develop a new driver, don't even try
|
||||
* to use any of these fields/hooks, and if you're modifying an
|
||||
* existing driver that is using those fields/hooks, you should
|
||||
* consider reworking the driver and avoid using them.
|
||||
* @options: Various chip options. They can partly be set to inform nand_scan
|
||||
* about special functionality. See the defines for further
|
||||
* explanation.
|
||||
* @current_interface_config: The currently used NAND interface configuration
|
||||
* @best_interface_config: The best NAND interface configuration which fits both
|
||||
* the NAND chip and NAND controller constraints. If
|
||||
* unset, the default reset interface configuration must
|
||||
* be used.
|
||||
* @bbt_erase_shift: Number of address bits in a bbt entry
|
||||
* @bbt_options: Bad block table specific options. All options used here must
|
||||
* come from bbm.h. By default, these options will be copied to
|
||||
* the appropriate nand_bbt_descr's.
|
||||
* @badblockpos: Bad block marker position in the oob area
|
||||
* @badblockbits: Minimum number of set bits in a good block's bad block marker
|
||||
* position; i.e., BBM = 11110111b is good when badblockbits = 7
|
||||
* @bbt_td: Bad block table descriptor for flash lookup
|
||||
* @bbt_md: Bad block table mirror descriptor
|
||||
* @badblock_pattern: Bad block scan pattern used for initial bad block scan
|
||||
* @bbt: Bad block table pointer
|
||||
* @page_shift: Number of address bits in a page (column address bits)
|
||||
* @phys_erase_shift: Number of address bits in a physical eraseblock
|
||||
* @chip_shift: Number of address bits in one chip
|
||||
* @pagemask: Page number mask = number of (pages / chip) - 1
|
||||
* @subpagesize: Holds the subpagesize
|
||||
* @data_buf: Buffer for data, size is (page size + oobsize)
|
||||
* @oob_poi: pointer on the OOB area covered by data_buf
|
||||
* @pagecache: Structure containing page cache related fields
|
||||
* @pagecache.bitflips: Number of bitflips of the cached page
|
||||
* @pagecache.page: Page number currently in the cache. -1 means no page is
|
||||
* currently cached
|
||||
* @buf_align: Minimum buffer alignment required by a platform
|
||||
* @lock: Lock protecting the suspended field. Also used to serialize accesses
|
||||
* to the NAND device
|
||||
* @suspended: Set to 1 when the device is suspended, 0 when it's not
|
||||
* @cur_cs: Currently selected target. -1 means no target selected, otherwise we
|
||||
* should always have cur_cs >= 0 && cur_cs < nanddev_ntargets().
|
||||
* NAND Controller drivers should not modify this value, but they're
|
||||
* allowed to read it.
|
||||
* @read_retries: The number of read retry modes supported
|
||||
* @controller: The hardware controller structure which is shared among multiple
|
||||
* independent devices
|
||||
* @ecc: The ECC controller structure
|
||||
* @priv: Chip private data
|
||||
*/
|
||||
struct nand_chip {
|
||||
struct nand_device base;
|
||||
|
||||
struct nand_id id;
|
||||
struct nand_parameters parameters;
|
||||
struct nand_manufacturer manufacturer;
|
||||
struct nand_chip_ops ops;
|
||||
struct nand_legacy legacy;
|
||||
|
||||
int (*setup_read_retry)(struct nand_chip *chip, int retry_mode);
|
||||
|
||||
unsigned int options;
|
||||
|
||||
/* Data interface */
|
||||
const struct nand_interface_config *current_interface_config;
|
||||
struct nand_interface_config *best_interface_config;
|
||||
|
||||
/* Bad block information */
|
||||
unsigned int bbt_erase_shift;
|
||||
unsigned int bbt_options;
|
||||
unsigned int badblockpos;
|
||||
unsigned int badblockbits;
|
||||
struct nand_bbt_descr *bbt_td;
|
||||
struct nand_bbt_descr *bbt_md;
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
u8 *bbt;
|
||||
|
||||
int page_shift;
|
||||
int phys_erase_shift;
|
||||
int bbt_erase_shift;
|
||||
int chip_shift;
|
||||
int pagemask;
|
||||
/* Device internal layout */
|
||||
unsigned int page_shift;
|
||||
unsigned int phys_erase_shift;
|
||||
unsigned int chip_shift;
|
||||
unsigned int pagemask;
|
||||
unsigned int subpagesize;
|
||||
|
||||
/* Buffers */
|
||||
u8 *data_buf;
|
||||
|
||||
u8 *oob_poi;
|
||||
struct {
|
||||
unsigned int bitflips;
|
||||
int page;
|
||||
} pagecache;
|
||||
|
||||
int subpagesize;
|
||||
int onfi_timing_mode_default;
|
||||
unsigned int badblockpos;
|
||||
int badblockbits;
|
||||
|
||||
struct nand_id id;
|
||||
struct nand_parameters parameters;
|
||||
|
||||
struct nand_data_interface data_interface;
|
||||
|
||||
int cur_cs;
|
||||
|
||||
int read_retries;
|
||||
|
||||
struct mutex lock;
|
||||
unsigned int suspended : 1;
|
||||
int (*suspend)(struct nand_chip *chip);
|
||||
void (*resume)(struct nand_chip *chip);
|
||||
|
||||
uint8_t *oob_poi;
|
||||
struct nand_controller *controller;
|
||||
|
||||
struct nand_ecc_ctrl ecc;
|
||||
unsigned long buf_align;
|
||||
|
||||
uint8_t *bbt;
|
||||
struct nand_bbt_descr *bbt_td;
|
||||
struct nand_bbt_descr *bbt_md;
|
||||
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
/* Internals */
|
||||
struct mutex lock;
|
||||
unsigned int suspended : 1;
|
||||
int cur_cs;
|
||||
int read_retries;
|
||||
|
||||
/* Externals */
|
||||
struct nand_controller *controller;
|
||||
struct nand_ecc_ctrl ecc;
|
||||
void *priv;
|
||||
|
||||
struct {
|
||||
const struct nand_manufacturer *desc;
|
||||
void *priv;
|
||||
} manufacturer;
|
||||
|
||||
int (*lock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
|
||||
int (*unlock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len);
|
||||
};
|
||||
|
||||
extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;
|
||||
@ -1209,6 +1206,17 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
|
||||
return mtd_get_of_node(nand_to_mtd(chip));
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_get_interface_config - Retrieve the current interface configuration
|
||||
* of a NAND chip
|
||||
* @chip: The NAND chip
|
||||
*/
|
||||
static inline const struct nand_interface_config *
|
||||
nand_get_interface_config(struct nand_chip *chip)
|
||||
{
|
||||
return chip->current_interface_config;
|
||||
}
|
||||
|
||||
/*
|
||||
* A helper for defining older NAND chips where the second ID byte fully
|
||||
* defined the chip, including the geometry (chip size, eraseblock size, page
|
||||
@ -1261,10 +1269,6 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
|
||||
* @ecc_step_ds in nand_chip{}, also from the datasheet.
|
||||
* For example, the "4bit ECC for each 512Byte" can be set with
|
||||
* NAND_ECC_INFO(4, 512).
|
||||
* @onfi_timing_mode_default: the default ONFI timing mode entered after a NAND
|
||||
* reset. Should be deduced from timings described
|
||||
* in the datasheet.
|
||||
*
|
||||
*/
|
||||
struct nand_flash_dev {
|
||||
char *name;
|
||||
@ -1285,7 +1289,6 @@ struct nand_flash_dev {
|
||||
uint16_t strength_ds;
|
||||
uint16_t step_ds;
|
||||
} ecc;
|
||||
int onfi_timing_mode_default;
|
||||
};
|
||||
|
||||
int nand_create_bbt(struct nand_chip *chip);
|
||||
|
@ -309,7 +309,7 @@ struct spinand_info {
|
||||
struct spinand_devid devid;
|
||||
u32 flags;
|
||||
struct nand_memory_organization memorg;
|
||||
struct nand_ecc_req eccreq;
|
||||
struct nand_ecc_props eccreq;
|
||||
struct spinand_ecc_info eccinfo;
|
||||
struct {
|
||||
const struct spinand_op_variants *read_cache;
|
||||
|
@ -262,7 +262,7 @@ struct mtd_ecc_stats {
|
||||
* @MTD_FILE_MODE_OTP_USER: OTP enabled in user mode
|
||||
* @MTD_FILE_MODE_RAW: OTP disabled, ECC disabled
|
||||
*
|
||||
* These modes can be set via ioctl(MTDFILEMODE). The mode mode will be retained
|
||||
* These modes can be set via ioctl(MTDFILEMODE). The mode will be retained
|
||||
* separately for each open file descriptor.
|
||||
*
|
||||
* Note: %MTD_FILE_MODE_RAW provides the same functionality as %MTD_OPS_RAW -
|
||||
|
Loading…
x
Reference in New Issue
Block a user