mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-30 11:04:25 +00:00
JFFS2 changes:
- Support 64-bit timestamps MTD changes: Core changes: - Support sub-partitions - Clarify mtd_oob_ops documentation - Make Kconfig formatting consistent - Fix potential overflows in mtdchar_{write,read}() - Fallback to ->_{read,write}() when ->_{read,write}_oob() is missing and no OOB data were requested - Remove VLA usage in the bch lib Driver changes: - Use mtd_device_register() instead of mtd_device_parse_register() where applicable - Use proper printk format to print physical addresses in the solutionengine driver - Add missing mtd_set_of_node() call in the powernv driver - Remove unneeded variables in a few drivers - Plug the TRX part parser to the DT partition parsers logic - Check ioremap_cache() return code in the gpio-addr-flash driver - Stop using VMLINUX_SYMBOL_STR() in gen_probe.c SPI NOR changes: Core changes: - Apply reset hacks only when reset is explicitly marked as broken in the DT Driver changes: - Minor cleanup/fixes in the m25p80 driver - Release flash_np in the nxp-spifi driver - Add suspend/resume hooks to the atmel-quadspi driver - Include gpio/consumer.h instead of gpio.h in the atmel-quadspi driver - Use %pK instead of %p in the stm32-quadspi driver - Improve timeout handling in the cadence-quadspi driver - Use mtd_device_register() instead of mtd_device_parse_register() in the intel-spi driver NAND changes: Core changes: - Add the SPI-NAND framework. - Create a helper to find the best ECC configuration. - Create NAND controller operations. - Allocate dynamically ONFI parameters structure. - Add defines for ONFI version bits. - Add manufacturer fixup for ONFI parameter page. - Add an option to specify NAND chip as a boot device. - Add Reed-Solomon error correction algorithm. - Better name for the controller structure. - Remove unused caller_is_module() definition. - Make subop helpers return unsigned values. - Expose _notsupp() helpers for raw page accessors. - Add default values for dynamic timings. - Kill the chip->scan_bbt() hook. - Rename nand_default_bbt() into nand_create_bbt(). - Start to clean the nand_chip structure. - Remove stale prototype from rawnand.h. Raw NAND controllers drivers changes: - Qcom: structuring cleanup. - Denali: use core helper to find the best ECC configuration. - Possible build of almost all drivers by adding a dependency on COMPILE_TEST for almost all of them in Kconfig, implies various fixes, Kconfig cleanup, GPIO headers inclusion cleanup, and even changes in sparc64 and ia64 architectures. - Clean the ->probe() functions error path of a lot of drivers. - Migrate all drivers to use nand_scan() instead of nand_scan_ident()/nand_scan_tail() pair. - Use mtd_device_register() where applicable to simplify the code. - Marvell: * Handle on-die ECC. * Better clocks handling. * Remove bogus comment. * Add suspend and resume support. - Tegra: add NAND controller driver. - Atmel: * Add module param to avoid using dma. * Drop Wenyou Yang from MAINTAINERS. - Denali: optimize timings handling. - FSMC: Stop using chip->read_buf(). - FSL: * Switch to SPDX license tag identifiers. * Fix qualifiers in MXC init functions. Raw NAND chip drivers changes: - Micron: * Add fixup for ONFI revision. * Update ecc_stats.corrected. * Make ECC activation stateful. * Avoid enabling/disabling ECC when it can't be disabled. * Get the actual number of bitflips. * Allow forced on-die ECC. * Support 8/512 on-die ECC. * Fix on-die ECC detection logic. - Hynix: * Fix decoding the OOB size on H27UCG8T2BTR. * Use ->exec_op() in hynix_nand_reg_write_op(). -----BEGIN PGP SIGNATURE----- iQI5BAABCAAjBQJbcSYdHBxib3Jpcy5icmV6aWxsb25AYm9vdGxpbi5jb20ACgkQ Ze02AX4ItwA85Q//ViUNiWoluaj7q3Kpv44NZZClPjVfirDtIHEfyzob7HbkrqjT ivjNUu5cjKU2aqptR24UzIV2lwmaSGicIzvyANN5bNedHy8tb4BWXo1iTEflXc1P lYihrRz7AeHQqdrOYAiHxq0fcqgIm2NjCDxfdtkP1HJsMQH+LgvoosOkd8RyLF8E jV5IyreTfg10AiVS44yvnqdsYo7GDVZRgnx1dBRbdJ+WwZS05m6LSeNu0ivgfLMM NmISrn7pzlMYnuDkzckXwVka7STFhV9befoSCmAHedqNEHtzaGDaNMLEilaC4WzV 1yvsdRGI3hDkdO4s44nP/Vs2XlVmCevuTpOOQ1dEK94Bbek+us7NEPjxOIAf22jE 5MVZR6vpNN7JXJ+nvEZjjL8X9zysqZgk2r6os35Wi2zJK7iHIrFpjlMYwTJfVPzv JKabOQZuOUqRACbL0isTTdG0+Tzr1BxaQqFiFUteQ9QpG4nSifoHUmI+9XUCJmil iGTOy6rEkaMd6qu3xapPtZbxGbUbvcLiJvNrZ02ZfsELDAELowz66O0PX6rSwTV9 NIWeVNXK9Bsp0oxbb8P8oJIB5K4F6vqncPBls34J7E/xrLO9nLk1VvFQrGqzuwwP HmEdgcRwKeXrvJITZz2u0u6lrKpgdDA3FTqfGCNec51PCgb1FwxcpKF/Xz0= =yIkF -----END PGP SIGNATURE----- Merge tag 'mtd/for-4.19' of git://git.infradead.org/linux-mtd Pull mtd updates from Boris Brezillon: "JFFS2 changes: - Support 64-bit timestamps MTD core changes: - Support sub-partitions - Clarify mtd_oob_ops documentation - Make Kconfig formatting consistent - Fix potential overflows in mtdchar_{write,read}() - Fallback to ->_{read,write}() when ->_{read,write}_oob() is missing and no OOB data were requested - Remove VLA usage in the bch lib MTD driver changes: - Use mtd_device_register() instead of mtd_device_parse_register() where applicable - Use proper printk format to print physical addresses in the solutionengine driver - Add missing mtd_set_of_node() call in the powernv driver - Remove unneeded variables in a few drivers - Plug the TRX part parser to the DT partition parsers logic - Check ioremap_cache() return code in the gpio-addr-flash driver - Stop using VMLINUX_SYMBOL_STR() in gen_probe.c SPI NOR core changes: - Apply reset hacks only when reset is explicitly marked as broken in the DT SPI NOR driver changes: - Minor cleanup/fixes in the m25p80 driver - Release flash_np in the nxp-spifi driver - Add suspend/resume hooks to the atmel-quadspi driver - Include gpio/consumer.h instead of gpio.h in the atmel-quadspi driver - Use %pK instead of %p in the stm32-quadspi driver - Improve timeout handling in the cadence-quadspi driver - Use mtd_device_register() instead of mtd_device_parse_register() in the intel-spi driver NAND core changes: - Add the SPI-NAND framework. - Create a helper to find the best ECC configuration. - Create NAND controller operations. - Allocate dynamically ONFI parameters structure. - Add defines for ONFI version bits. - Add manufacturer fixup for ONFI parameter page. - Add an option to specify NAND chip as a boot device. - Add Reed-Solomon error correction algorithm. - Better name for the controller structure. - Remove unused caller_is_module() definition. - Make subop helpers return unsigned values. - Expose _notsupp() helpers for raw page accessors. - Add default values for dynamic timings. - Kill the chip->scan_bbt() hook. - Rename nand_default_bbt() into nand_create_bbt(). - Start to clean the nand_chip structure. - Remove stale prototype from rawnand.h. Raw NAND controllers drivers changes: - Qcom: structuring cleanup. - Denali: use core helper to find the best ECC configuration. - Possible build of almost all drivers by adding a dependency on COMPILE_TEST for almost all of them in Kconfig, implies various fixes, Kconfig cleanup, GPIO headers inclusion cleanup, and even changes in sparc64 and ia64 architectures. - Clean the ->probe() functions error path of a lot of drivers. - Migrate all drivers to use nand_scan() instead of nand_scan_ident()/nand_scan_tail() pair. - Use mtd_device_register() where applicable to simplify the code. - Marvell: * Handle on-die ECC. * Better clocks handling. * Remove bogus comment. * Add suspend and resume support. - Tegra: add NAND controller driver. - Atmel: * Add module param to avoid using dma. * Drop Wenyou Yang from MAINTAINERS. - Denali: optimize timings handling. - FSMC: Stop using chip->read_buf(). - FSL: * Switch to SPDX license tag identifiers. * Fix qualifiers in MXC init functions. Raw NAND chip drivers changes: - Micron: * Add fixup for ONFI revision. * Update ecc_stats.corrected. * Make ECC activation stateful. * Avoid enabling/disabling ECC when it can't be disabled. * Get the actual number of bitflips. * Allow forced on-die ECC. * Support 8/512 on-die ECC. * Fix on-die ECC detection logic. - Hynix: * Fix decoding the OOB size on H27UCG8T2BTR. * Use ->exec_op() in hynix_nand_reg_write_op()" * tag 'mtd/for-4.19' of git://git.infradead.org/linux-mtd: (188 commits) mtd: rawnand: atmel: Select GENERIC_ALLOCATOR MAINTAINERS: drop Wenyou Yang from Atmel NAND driver support mtd: rawnand: allocate dynamically ONFI parameters during detection mtd: spi-nor: only apply reset hacks to broken hardware mtd: spi-nor: cadence-quadspi: fix timeout handling mtd: spi-nor: atmel-quadspi: Include gpio/consumer.h instead of gpio.h mtd: spi-nor: intel-spi: use mtd_device_register() mtd: spi-nor: stm32-quadspi: replace "%p" with "%pK" mtd: spi-nor: atmel-quadspi: add suspend/resume hooks mtd: rawnand: allocate model parameter dynamically mtd: rawnand: do not export nand_scan_[ident|tail]() anymore mtd: rawnand: txx9ndfmc: convert driver to nand_scan() mtd: rawnand: txx9ndfmc: clarify ECC parameters assignation mtd: rawnand: tegra: convert driver to nand_scan() mtd: rawnand: jz4740: convert driver to nand_scan() mtd: rawnand: jz4740: group nand_scan_{ident, tail} calls mtd: rawnand: jz4740: fix probe function error path mtd: rawnand: docg4: convert driver to nand_scan() mtd: rawnand: do not execute nand_scan_ident() if maxchips is zero mtd: rawnand: atmel: convert driver to nand_scan() ...
This commit is contained in:
commit
c2fc71c9b7
121 changed files with 6537 additions and 2264 deletions
|
@ -8,6 +8,9 @@ Required properties:
|
|||
- reg : should contain registers location and length for data and reg.
|
||||
- reg-names: Should contain the reg names "nand_data" and "denali_reg"
|
||||
- interrupts : The interrupt number.
|
||||
- clocks: should contain phandle of the controller core clock, the bus
|
||||
interface clock, and the ECC circuit clock.
|
||||
- clock-names: should contain "nand", "nand_x", "ecc"
|
||||
|
||||
Optional properties:
|
||||
- nand-ecc-step-size: see nand.txt for details. If present, the value must be
|
||||
|
@ -31,5 +34,7 @@ nand: nand@ff900000 {
|
|||
compatible = "altr,socfpga-denali-nand";
|
||||
reg = <0xff900000 0x20>, <0xffb80000 0x1000>;
|
||||
reg-names = "nand_data", "denali_reg";
|
||||
clocks = <&nand_clk>, <&nand_x_clk>, <&nand_ecc_clk>;
|
||||
clock-names = "nand", "nand_x", "ecc";
|
||||
interrupts = <0 144 4>;
|
||||
};
|
||||
|
|
|
@ -69,6 +69,15 @@ Optional properties:
|
|||
all chips and support for it can not be detected at runtime.
|
||||
Refer to your chips' datasheet to check if this is supported
|
||||
by your chip.
|
||||
- broken-flash-reset : Some flash devices utilize stateful addressing modes
|
||||
(e.g., for 32-bit addressing) which need to be managed
|
||||
carefully by a system. Because these sorts of flash don't
|
||||
have a standardized software reset command, and because some
|
||||
systems don't toggle the flash RESET# pin upon system reset
|
||||
(if the pin even exists at all), there are systems which
|
||||
cannot reboot properly if the flash is left in the "wrong"
|
||||
state. This boolean flag can be used on such systems, to
|
||||
denote the absence of a reliable reset mechanism.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ Optional NAND chip properties:
|
|||
Deprecated values:
|
||||
"soft_bch": use "soft" and nand-ecc-algo instead
|
||||
- nand-ecc-algo: string, algorithm of NAND ECC.
|
||||
Supported values are: "hamming", "bch".
|
||||
Valid values are: "hamming", "bch", "rs".
|
||||
- nand-bus-width : 8 or 16 bus width if not present 8
|
||||
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
|
||||
|
||||
|
@ -43,6 +43,10 @@ Optional NAND chip properties:
|
|||
This is particularly useful when only the in-band area is
|
||||
used by the upper layers, and you want to make your NAND
|
||||
as reliable as possible.
|
||||
- nand-is-boot-medium: Whether the NAND chip is a boot medium. Drivers might use
|
||||
this information to select ECC algorithms supported by
|
||||
the boot ROM or similar restrictions.
|
||||
|
||||
- nand-rb: shall contain the native Ready/Busy ids.
|
||||
|
||||
The ECC strength and ECC step size properties define the correction capability
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
NVIDIA Tegra NAND Flash controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of:
|
||||
- "nvidia,tegra20-nand"
|
||||
- reg: MMIO address range
|
||||
- interrupts: interrupt output of the NFC controller
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: Must include the following entries:
|
||||
- nand
|
||||
- resets: Must contain an entry for each entry in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names: Must include the following entries:
|
||||
- nand
|
||||
|
||||
Optional children nodes:
|
||||
Individual NAND chips are children of the NAND controller node. Currently
|
||||
only one NAND chip supported.
|
||||
|
||||
Required children node properties:
|
||||
- reg: An integer ranging from 1 to 6 representing the CS line to use.
|
||||
|
||||
Optional children node properties:
|
||||
- nand-ecc-mode: String, operation mode of the NAND ecc mode. Currently only
|
||||
"hw" is supported.
|
||||
- nand-ecc-algo: string, algorithm of NAND ECC.
|
||||
Supported values with "hw" ECC mode are: "rs", "bch".
|
||||
- nand-bus-width : See nand.txt
|
||||
- nand-on-flash-bbt: See nand.txt
|
||||
- nand-ecc-strength: integer representing the number of bits to correct
|
||||
per ECC step (always 512). Supported strength using HW ECC
|
||||
modes are:
|
||||
- RS: 4, 6, 8
|
||||
- BCH: 4, 8, 14, 16
|
||||
- nand-ecc-maximize: See nand.txt
|
||||
- nand-is-boot-medium: Makes sure only ECC strengths supported by the boot ROM
|
||||
are chosen.
|
||||
- wp-gpios: GPIO specifier for the write protect pin.
|
||||
|
||||
Optional child node of NAND chip nodes:
|
||||
Partitions: see partition.txt
|
||||
|
||||
Example:
|
||||
nand-controller@70008000 {
|
||||
compatible = "nvidia,tegra20-nand";
|
||||
reg = <0x70008000 0x100>;
|
||||
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&tegra_car TEGRA20_CLK_NDFLASH>;
|
||||
clock-names = "nand";
|
||||
resets = <&tegra_car 13>;
|
||||
reset-names = "nand";
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
nand-bus-width = <8>;
|
||||
nand-on-flash-bbt;
|
||||
nand-ecc-algo = "bch";
|
||||
nand-ecc-strength = <8>;
|
||||
wp-gpios = <&gpio TEGRA_GPIO(S, 0) GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
|
@ -14,6 +14,13 @@ method is used for a given flash device. To describe the method there should be
|
|||
a subnode of the flash device that is named 'partitions'. It must have a
|
||||
'compatible' property, which is used to identify the method to use.
|
||||
|
||||
When a single partition is represented with a DT node (it depends on a used
|
||||
format) it may also be described using above rules ('compatible' and optionally
|
||||
some extra properties / subnodes). It allows describing more complex,
|
||||
hierarchical (multi-level) layouts and should be used if there is some
|
||||
significant relation between partitions or some partition internally uses
|
||||
another partitioning method.
|
||||
|
||||
Available bindings are listed in the "partitions" subdirectory.
|
||||
|
||||
|
||||
|
@ -109,3 +116,42 @@ flash@2 {
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
flash@3 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "bootloader";
|
||||
reg = <0x000000 0x100000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
firmware@100000 {
|
||||
label = "firmware";
|
||||
reg = <0x100000 0xe00000>;
|
||||
compatible = "brcm,trx";
|
||||
};
|
||||
|
||||
calibration@f00000 {
|
||||
label = "calibration";
|
||||
reg = <0xf00000 0x100000>;
|
||||
compatible = "fixed-partitions";
|
||||
ranges = <0 0xf00000 0x100000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "wifi0";
|
||||
reg = <0x000000 0x080000>;
|
||||
};
|
||||
|
||||
partition@80000 {
|
||||
label = "wifi1";
|
||||
reg = <0x080000 0x080000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
Broadcom TRX Container Partition
|
||||
================================
|
||||
|
||||
TRX is Broadcom's official firmware format for the BCM947xx boards. It's used by
|
||||
most of the vendors building devices based on Broadcom's BCM47xx SoCs and is
|
||||
supported by the CFE bootloader.
|
||||
|
||||
Design of the TRX format is very minimalistic. Its header contains
|
||||
identification fields, CRC32 checksum and the locations of embedded partitions.
|
||||
Its purpose is to store a few partitions in a format that can be distributed as
|
||||
a standalone file and written in a flash memory.
|
||||
|
||||
Container can hold up to 4 partitions. The first partition has to contain a
|
||||
device executable binary (e.g. a kernel) as it's what the CFE bootloader starts
|
||||
executing. Other partitions can be used for operating system purposes. This is
|
||||
useful for systems that keep kernel and rootfs separated.
|
||||
|
||||
TRX doesn't enforce any strict partition boundaries or size limits. All
|
||||
partitions have to be less than the 4GiB max size limit.
|
||||
|
||||
There are two existing/known TRX variants:
|
||||
1) v1 which contains 3 partitions
|
||||
2) v2 which contains 4 partitions
|
||||
|
||||
There aren't separated compatible bindings for them as version can be trivialy
|
||||
detected by a software parsing TRX header.
|
||||
|
||||
Required properties:
|
||||
- compatible : (required) must be "brcm,trx"
|
||||
|
||||
Example:
|
||||
|
||||
flash@0 {
|
||||
partitions {
|
||||
compatible = "brcm,trx";
|
||||
};
|
||||
};
|
|
@ -45,11 +45,12 @@ Required properties:
|
|||
number (e.g., 0, 1, 2, etc.)
|
||||
- #address-cells: see partition.txt
|
||||
- #size-cells: see partition.txt
|
||||
- nand-ecc-strength: see nand.txt
|
||||
- nand-ecc-step-size: must be 512. see nand.txt for more details.
|
||||
|
||||
Optional properties:
|
||||
- nand-bus-width: see nand.txt
|
||||
- nand-ecc-strength: see nand.txt. If not specified, then ECC strength will
|
||||
be used according to chip requirement and available
|
||||
OOB size.
|
||||
|
||||
Each nandcs device node may optionally contain a 'partitions' sub-node, which
|
||||
further contains sub-nodes describing the flash partition mapping. See
|
||||
|
@ -77,7 +78,6 @@ nand-controller@1ac00000 {
|
|||
reg = <0>;
|
||||
|
||||
nand-ecc-strength = <4>;
|
||||
nand-ecc-step-size = <512>;
|
||||
nand-bus-width = <8>;
|
||||
|
||||
partitions {
|
||||
|
@ -117,7 +117,6 @@ nand-controller@79b0000 {
|
|||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-ecc-strength = <4>;
|
||||
nand-ecc-step-size = <512>;
|
||||
nand-bus-width = <8>;
|
||||
|
||||
partitions {
|
||||
|
|
5
Documentation/devicetree/bindings/mtd/spi-nand.txt
Normal file
5
Documentation/devicetree/bindings/mtd/spi-nand.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
SPI NAND flash
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "spi-nand"
|
||||
- reg: should encode the chip-select line used to access the NAND chip
|
|
@ -9353,7 +9353,6 @@ F: drivers/media/platform/atmel/atmel-isc-regs.h
|
|||
F: devicetree/bindings/media/atmel-isc.txt
|
||||
|
||||
MICROCHIP / ATMEL NAND DRIVER
|
||||
M: Wenyou Yang <wenyou.yang@microchip.com>
|
||||
M: Josh Wu <rainyfeeling@outlook.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
S: Supported
|
||||
|
@ -14063,6 +14062,13 @@ M: Laxman Dewangan <ldewangan@nvidia.com>
|
|||
S: Supported
|
||||
F: drivers/input/keyboard/tegra-kbc.c
|
||||
|
||||
TEGRA NAND DRIVER
|
||||
M: Stefan Agner <stefan@agner.ch>
|
||||
M: Lucas Stach <dev@lynxeye.de>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/mtd/nvidia-tegra20-nand.txt
|
||||
F: drivers/mtd/nand/raw/tegra_nand.c
|
||||
|
||||
TEGRA PWM DRIVER
|
||||
M: Thierry Reding <thierry.reding@gmail.com>
|
||||
S: Supported
|
||||
|
|
|
@ -688,7 +688,6 @@ struct platform_nand_data balloon3_nand_pdata = {
|
|||
.chip_delay = 50,
|
||||
},
|
||||
.ctrl = {
|
||||
.hwcontrol = 0,
|
||||
.dev_ready = balloon3_nand_dev_ready,
|
||||
.select_chip = balloon3_nand_select_chip,
|
||||
.cmd_ctrl = balloon3_nand_cmd_ctl,
|
||||
|
|
|
@ -346,7 +346,6 @@ struct platform_nand_data em_x270_nand_platdata = {
|
|||
.chip_delay = 20,
|
||||
},
|
||||
.ctrl = {
|
||||
.hwcontrol = 0,
|
||||
.dev_ready = em_x270_nand_device_ready,
|
||||
.select_chip = 0,
|
||||
.cmd_ctrl = em_x270_nand_cmd_ctl,
|
||||
|
|
|
@ -83,12 +83,14 @@ virt_to_phys (volatile void *address)
|
|||
{
|
||||
return (unsigned long) address - PAGE_OFFSET;
|
||||
}
|
||||
#define virt_to_phys virt_to_phys
|
||||
|
||||
static inline void*
|
||||
phys_to_virt (unsigned long address)
|
||||
{
|
||||
return (void *) (address + PAGE_OFFSET);
|
||||
}
|
||||
#define phys_to_virt phys_to_virt
|
||||
|
||||
#define ARCH_HAS_VALID_PHYS_ADDR_RANGE
|
||||
extern u64 kern_mem_attribute (unsigned long phys_addr, unsigned long size);
|
||||
|
@ -433,9 +435,11 @@ static inline void __iomem * ioremap_cache (unsigned long phys_addr, unsigned lo
|
|||
{
|
||||
return ioremap(phys_addr, size);
|
||||
}
|
||||
#define ioremap ioremap
|
||||
#define ioremap_nocache ioremap_nocache
|
||||
#define ioremap_cache ioremap_cache
|
||||
#define ioremap_uc ioremap_nocache
|
||||
|
||||
#define iounmap iounmap
|
||||
|
||||
/*
|
||||
* String version of IO memory access ops:
|
||||
|
@ -444,6 +448,13 @@ extern void memcpy_fromio(void *dst, const volatile void __iomem *src, long n);
|
|||
extern void memcpy_toio(volatile void __iomem *dst, const void *src, long n);
|
||||
extern void memset_io(volatile void __iomem *s, int c, long n);
|
||||
|
||||
#define memcpy_fromio memcpy_fromio
|
||||
#define memcpy_toio memcpy_toio
|
||||
#define memset_io memset_io
|
||||
#define xlate_dev_kmem_ptr xlate_dev_kmem_ptr
|
||||
#define xlate_dev_mem_ptr xlate_dev_mem_ptr
|
||||
#include <asm-generic/io.h>
|
||||
|
||||
# endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _ASM_IA64_IO_H */
|
||||
|
|
|
@ -29,10 +29,11 @@
|
|||
#include <linux/power/gpio-charger.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
#include <linux/platform_data/jz4740/jz4740_nand.h>
|
||||
|
||||
#include <asm/mach-jz4740/gpio.h>
|
||||
#include <asm/mach-jz4740/jz4740_fb.h>
|
||||
#include <asm/mach-jz4740/jz4740_mmc.h>
|
||||
#include <asm/mach-jz4740/jz4740_nand.h>
|
||||
|
||||
#include <linux/regulator/fixed.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/txx9/ndfmc.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/leds.h>
|
||||
|
@ -36,7 +37,6 @@
|
|||
#include <asm/txx9/generic.h>
|
||||
#include <asm/txx9/pci.h>
|
||||
#include <asm/txx9tmr.h>
|
||||
#include <asm/txx9/ndfmc.h>
|
||||
#include <asm/txx9/dmac.h>
|
||||
#ifdef CONFIG_CPU_TX49XX
|
||||
#include <asm/txx9/tx4938.h>
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
#include <linux/ptrace.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/txx9/ndfmc.h>
|
||||
#include <asm/reboot.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/txx9irq.h>
|
||||
#include <asm/txx9tmr.h>
|
||||
#include <asm/txx9pio.h>
|
||||
#include <asm/txx9/generic.h>
|
||||
#include <asm/txx9/ndfmc.h>
|
||||
#include <asm/txx9/dmac.h>
|
||||
#include <asm/txx9/tx4938.h>
|
||||
|
||||
|
|
|
@ -21,13 +21,13 @@
|
|||
#include <linux/ptrace.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/txx9/ndfmc.h>
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/reboot.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/txx9irq.h>
|
||||
#include <asm/txx9tmr.h>
|
||||
#include <asm/txx9/generic.h>
|
||||
#include <asm/txx9/ndfmc.h>
|
||||
#include <asm/txx9/dmac.h>
|
||||
#include <asm/txx9/tx4939.h>
|
||||
|
||||
|
|
|
@ -243,35 +243,42 @@ void insb(unsigned long, void *, unsigned long);
|
|||
void insw(unsigned long, void *, unsigned long);
|
||||
void insl(unsigned long, void *, unsigned long);
|
||||
|
||||
static inline void ioread8_rep(void __iomem *port, void *buf, unsigned long count)
|
||||
static inline void readsb(void __iomem *port, void *buf, unsigned long count)
|
||||
{
|
||||
insb((unsigned long __force)port, buf, count);
|
||||
}
|
||||
static inline void ioread16_rep(void __iomem *port, void *buf, unsigned long count)
|
||||
static inline void readsw(void __iomem *port, void *buf, unsigned long count)
|
||||
{
|
||||
insw((unsigned long __force)port, buf, count);
|
||||
}
|
||||
|
||||
static inline void ioread32_rep(void __iomem *port, void *buf, unsigned long count)
|
||||
static inline void readsl(void __iomem *port, void *buf, unsigned long count)
|
||||
{
|
||||
insl((unsigned long __force)port, buf, count);
|
||||
}
|
||||
|
||||
static inline void iowrite8_rep(void __iomem *port, const void *buf, unsigned long count)
|
||||
static inline void writesb(void __iomem *port, const void *buf, unsigned long count)
|
||||
{
|
||||
outsb((unsigned long __force)port, buf, count);
|
||||
}
|
||||
|
||||
static inline void iowrite16_rep(void __iomem *port, const void *buf, unsigned long count)
|
||||
static inline void writesw(void __iomem *port, const void *buf, unsigned long count)
|
||||
{
|
||||
outsw((unsigned long __force)port, buf, count);
|
||||
}
|
||||
|
||||
static inline void iowrite32_rep(void __iomem *port, const void *buf, unsigned long count)
|
||||
static inline void writesl(void __iomem *port, const void *buf, unsigned long count)
|
||||
{
|
||||
outsl((unsigned long __force)port, buf, count);
|
||||
}
|
||||
|
||||
#define ioread8_rep(p,d,l) readsb(p,d,l)
|
||||
#define ioread16_rep(p,d,l) readsw(p,d,l)
|
||||
#define ioread32_rep(p,d,l) readsl(p,d,l)
|
||||
#define iowrite8_rep(p,d,l) writesb(p,d,l)
|
||||
#define iowrite16_rep(p,d,l) writesw(p,d,l)
|
||||
#define iowrite32_rep(p,d,l) writesl(p,d,l)
|
||||
|
||||
/* Valid I/O Space regions are anywhere, because each PCI bus supported
|
||||
* can live in an arbitrary area of the physical address range.
|
||||
*/
|
||||
|
|
|
@ -30,6 +30,7 @@ config BCMA_HOST_PCI
|
|||
|
||||
config BCMA_HOST_SOC
|
||||
bool "Support for BCMA in a SoC"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Host interface for a Broadcom AIX bus directly mapped into
|
||||
the memory. This only works with the Broadcom SoCs from the
|
||||
|
@ -61,7 +62,7 @@ config BCMA_DRIVER_PCI_HOSTMODE
|
|||
|
||||
config BCMA_DRIVER_MIPS
|
||||
bool "BCMA Broadcom MIPS core driver"
|
||||
depends on MIPS
|
||||
depends on MIPS || COMPILE_TEST
|
||||
help
|
||||
Driver for the Broadcom MIPS core attached to Broadcom specific
|
||||
Advanced Microcontroller Bus.
|
||||
|
|
|
@ -116,12 +116,14 @@ config FSL_CORENET_CF
|
|||
|
||||
config FSL_IFC
|
||||
bool
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
|
||||
config JZ4780_NEMC
|
||||
bool "Ingenic JZ4780 SoC NEMC driver"
|
||||
default y
|
||||
depends on MACH_JZ4780
|
||||
depends on MACH_JZ4780 || COMPILE_TEST
|
||||
depends on HAS_IOMEM && OF
|
||||
help
|
||||
This driver is for the NAND/External Memory Controller (NEMC) in
|
||||
the Ingenic JZ4780. This controller is used to handle external
|
||||
|
|
|
@ -24,7 +24,7 @@ config MTD_TESTS
|
|||
|
||||
config MTD_REDBOOT_PARTS
|
||||
tristate "RedBoot partition table parsing"
|
||||
---help---
|
||||
help
|
||||
RedBoot is a ROM monitor and bootloader which deals with multiple
|
||||
'images' in flash devices by putting a table one of the erase
|
||||
blocks on the device, similar to a partition table, which gives
|
||||
|
@ -45,7 +45,7 @@ if MTD_REDBOOT_PARTS
|
|||
config MTD_REDBOOT_DIRECTORY_BLOCK
|
||||
int "Location of RedBoot partition table"
|
||||
default "-1"
|
||||
---help---
|
||||
help
|
||||
This option is the Linux counterpart to the
|
||||
CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time
|
||||
option.
|
||||
|
@ -75,7 +75,7 @@ endif # MTD_REDBOOT_PARTS
|
|||
config MTD_CMDLINE_PARTS
|
||||
tristate "Command line partition table parsing"
|
||||
depends on MTD
|
||||
---help---
|
||||
help
|
||||
Allow generic configuration of the MTD partition tables via the kernel
|
||||
command line. Multiple flash resources are supported for hardware where
|
||||
different kinds of flash memory are available.
|
||||
|
@ -112,7 +112,7 @@ config MTD_CMDLINE_PARTS
|
|||
config MTD_AFS_PARTS
|
||||
tristate "ARM Firmware Suite partition parsing"
|
||||
depends on (ARM || ARM64)
|
||||
---help---
|
||||
help
|
||||
The ARM Firmware Suite allows the user to divide flash devices into
|
||||
multiple 'images'. Each such image has a header containing its name
|
||||
and offset/size etc.
|
||||
|
@ -136,7 +136,7 @@ config MTD_OF_PARTS
|
|||
|
||||
config MTD_AR7_PARTS
|
||||
tristate "TI AR7 partitioning support"
|
||||
---help---
|
||||
help
|
||||
TI AR7 partitioning support
|
||||
|
||||
config MTD_BCM63XX_PARTS
|
||||
|
@ -170,7 +170,7 @@ config MTD_BLOCK
|
|||
tristate "Caching block device access to MTD devices"
|
||||
depends on BLOCK
|
||||
select MTD_BLKDEVS
|
||||
---help---
|
||||
help
|
||||
Although most flash chips have an erase size too large to be useful
|
||||
as block devices, it is possible to use MTD devices which are based
|
||||
on RAM chips in this manner. This block device is a user of MTD
|
||||
|
@ -205,7 +205,7 @@ config FTL
|
|||
tristate "FTL (Flash Translation Layer) support"
|
||||
depends on BLOCK
|
||||
select MTD_BLKDEVS
|
||||
---help---
|
||||
help
|
||||
This provides support for the original Flash Translation Layer which
|
||||
is part of the PCMCIA specification. It uses a kind of pseudo-
|
||||
file system on a flash device to emulate a block device with
|
||||
|
@ -222,7 +222,7 @@ config NFTL
|
|||
tristate "NFTL (NAND Flash Translation Layer) support"
|
||||
depends on BLOCK
|
||||
select MTD_BLKDEVS
|
||||
---help---
|
||||
help
|
||||
This provides support for the NAND Flash Translation Layer which is
|
||||
used on M-Systems' DiskOnChip devices. It uses a kind of pseudo-
|
||||
file system on a flash device to emulate a block device with
|
||||
|
@ -246,7 +246,7 @@ config INFTL
|
|||
tristate "INFTL (Inverse NAND Flash Translation Layer) support"
|
||||
depends on BLOCK
|
||||
select MTD_BLKDEVS
|
||||
---help---
|
||||
help
|
||||
This provides support for the Inverse NAND Flash Translation
|
||||
Layer which is used on M-Systems' newer DiskOnChip devices. It
|
||||
uses a kind of pseudo-file system on a flash device to emulate
|
||||
|
@ -261,10 +261,10 @@ config INFTL
|
|||
not use it.
|
||||
|
||||
config RFD_FTL
|
||||
tristate "Resident Flash Disk (Flash Translation Layer) support"
|
||||
tristate "Resident Flash Disk (Flash Translation Layer) support"
|
||||
depends on BLOCK
|
||||
select MTD_BLKDEVS
|
||||
---help---
|
||||
help
|
||||
This provides support for the flash translation layer known
|
||||
as the Resident Flash Disk (RFD), as used by the Embedded BIOS
|
||||
of General Software. There is a blurb at:
|
||||
|
@ -308,7 +308,7 @@ config MTD_SWAP
|
|||
select MTD_BLKDEVS
|
||||
help
|
||||
Provides volatile block device driver on top of mtd partition
|
||||
suitable for swapping. The mapping of written blocks is not saved.
|
||||
suitable for swapping. The mapping of written blocks is not saved.
|
||||
The driver provides wear leveling by storing erase counter into the
|
||||
OOB.
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ choice
|
|||
prompt "Flash cmd/query data swapping"
|
||||
depends on MTD_CFI_ADV_OPTIONS
|
||||
default MTD_CFI_NOSWAP
|
||||
---help---
|
||||
help
|
||||
This option defines the way in which the CPU attempts to arrange
|
||||
data bits when writing the 'magic' commands to the chips. Saying
|
||||
'NO', which is the default when CONFIG_MTD_CFI_ADV_OPTIONS isn't
|
||||
|
|
|
@ -1216,7 +1216,6 @@ static inline int do_read_secsi_onechip(struct map_info *map,
|
|||
size_t grouplen)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
|
||||
retry:
|
||||
mutex_lock(&chip->mutex);
|
||||
|
@ -1229,7 +1228,6 @@ static inline int do_read_secsi_onechip(struct map_info *map,
|
|||
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
timeo = jiffies + HZ;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
|
|
@ -202,16 +202,19 @@ static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map,
|
|||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
|
||||
#ifdef CONFIG_MODULES
|
||||
char probename[sizeof(VMLINUX_SYMBOL_STR(cfi_cmdset_%4.4X))];
|
||||
cfi_cmdset_fn_t *probe_function;
|
||||
char *probename;
|
||||
|
||||
sprintf(probename, VMLINUX_SYMBOL_STR(cfi_cmdset_%4.4X), type);
|
||||
probename = kasprintf(GFP_KERNEL, "cfi_cmdset_%4.4X", type);
|
||||
if (!probename)
|
||||
return NULL;
|
||||
|
||||
probe_function = __symbol_get(probename);
|
||||
if (!probe_function) {
|
||||
request_module("cfi_cmdset_%4.4X", type);
|
||||
probe_function = __symbol_get(probename);
|
||||
}
|
||||
kfree(probename);
|
||||
|
||||
if (probe_function) {
|
||||
struct mtd_info *mtd;
|
||||
|
|
|
@ -5,7 +5,7 @@ menu "Self-contained MTD device drivers"
|
|||
config MTD_PMC551
|
||||
tristate "Ramix PMC551 PCI Mezzanine RAM card support"
|
||||
depends on PCI
|
||||
---help---
|
||||
help
|
||||
This provides a MTD device driver for the Ramix PMC551 RAM PCI card
|
||||
from Ramix Inc. <http://www.ramix.com/products/memory/pmc551.html>.
|
||||
These devices come in memory configurations from 32M - 1G. If you
|
||||
|
@ -209,7 +209,7 @@ config MTD_DOCG3
|
|||
select BCH
|
||||
select BCH_CONST_PARAMS
|
||||
select BITREVERSE
|
||||
---help---
|
||||
help
|
||||
This provides an MTD device driver for the M-Systems DiskOnChip
|
||||
G3 devices.
|
||||
|
||||
|
|
|
@ -28,11 +28,9 @@
|
|||
#include <linux/spi/flash.h>
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
|
||||
#define MAX_CMD_SIZE 6
|
||||
struct m25p {
|
||||
struct spi_mem *spimem;
|
||||
struct spi_nor spi_nor;
|
||||
u8 command[MAX_CMD_SIZE];
|
||||
};
|
||||
|
||||
static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
|
||||
|
@ -70,7 +68,7 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
|
|||
struct spi_mem_op op =
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
|
||||
SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
|
||||
SPI_MEM_OP_DUMMY(0, 1),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_OUT(len, buf, 1));
|
||||
size_t remaining = len;
|
||||
int ret;
|
||||
|
@ -78,7 +76,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
|
|||
/* get transfer protocols. */
|
||||
op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
|
||||
op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
|
||||
op.dummy.buswidth = op.addr.buswidth;
|
||||
op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
|
||||
|
||||
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
|
||||
|
|
|
@ -223,6 +223,7 @@ static int powernv_flash_set_driver_info(struct device *dev,
|
|||
mtd->_read = powernv_flash_read;
|
||||
mtd->_write = powernv_flash_write;
|
||||
mtd->dev.parent = dev;
|
||||
mtd_set_of_node(mtd, dev->of_node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -394,9 +394,8 @@ static int sst25l_probe(struct spi_device *spi)
|
|||
flash->mtd.numeraseregions);
|
||||
|
||||
|
||||
ret = mtd_device_parse_register(&flash->mtd, NULL, NULL,
|
||||
data ? data->parts : NULL,
|
||||
data ? data->nr_parts : 0);
|
||||
ret = mtd_device_register(&flash->mtd, data ? data->parts : NULL,
|
||||
data ? data->nr_parts : 0);
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
@ -13,10 +13,10 @@ config MTD_QINFO_PROBE
|
|||
depends on MTD_LPDDR
|
||||
tristate "Detect flash chips by QINFO probe"
|
||||
help
|
||||
Device Information for LPDDR chips is offered through the Overlay
|
||||
Window QINFO interface, permits software to be used for entire
|
||||
families of devices. This serves similar purpose of CFI on legacy
|
||||
Flash products
|
||||
Device Information for LPDDR chips is offered through the Overlay
|
||||
Window QINFO interface, permits software to be used for entire
|
||||
families of devices. This serves similar purpose of CFI on legacy
|
||||
Flash products
|
||||
|
||||
config MTD_LPDDR2_NVM
|
||||
# ARM dependency is only for writel_relaxed()
|
||||
|
|
|
@ -476,7 +476,7 @@ static int lpddr2_nvm_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
/* Parse partitions and register the MTD device */
|
||||
return mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
|
||||
return mtd_device_register(mtd, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -207,13 +207,13 @@ config MTD_ICHXROM
|
|||
BE VERY CAREFUL.
|
||||
|
||||
config MTD_ESB2ROM
|
||||
tristate "BIOS flash chip on Intel ESB Controller Hub 2"
|
||||
depends on X86 && MTD_JEDECPROBE && PCI
|
||||
help
|
||||
Support for treating the BIOS flash chip on ESB2 motherboards
|
||||
as an MTD device - with this you can reprogram your BIOS.
|
||||
tristate "BIOS flash chip on Intel ESB Controller Hub 2"
|
||||
depends on X86 && MTD_JEDECPROBE && PCI
|
||||
help
|
||||
Support for treating the BIOS flash chip on ESB2 motherboards
|
||||
as an MTD device - with this you can reprogram your BIOS.
|
||||
|
||||
BE VERY CAREFUL.
|
||||
BE VERY CAREFUL.
|
||||
|
||||
config MTD_CK804XROM
|
||||
tristate "BIOS flash chip on Nvidia CK804"
|
||||
|
@ -401,12 +401,12 @@ config MTD_PISMO
|
|||
When built as a module, it will be called pismo.ko
|
||||
|
||||
config MTD_LATCH_ADDR
|
||||
tristate "Latch-assisted Flash Chip Support"
|
||||
depends on MTD_COMPLEX_MAPPINGS
|
||||
help
|
||||
Map driver which allows flashes to be partially physically addressed
|
||||
and have the upper address lines set by a board specific code.
|
||||
tristate "Latch-assisted Flash Chip Support"
|
||||
depends on MTD_COMPLEX_MAPPINGS
|
||||
help
|
||||
Map driver which allows flashes to be partially physically addressed
|
||||
and have the upper address lines set by a board specific code.
|
||||
|
||||
If compiled as a module, it will be called latch-addr-flash.
|
||||
If compiled as a module, it will be called latch-addr-flash.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -239,6 +239,9 @@ static int gpio_flash_probe(struct platform_device *pdev)
|
|||
state->map.bankwidth = pdata->width;
|
||||
state->map.size = state->win_size * (1 << state->gpio_count);
|
||||
state->map.virt = ioremap_nocache(memory->start, state->map.size);
|
||||
if (!state->map.virt)
|
||||
return -ENOMEM;
|
||||
|
||||
state->map.phys = NO_XIP;
|
||||
state->map.map_priv_1 = (unsigned long)state;
|
||||
|
||||
|
|
|
@ -88,9 +88,8 @@ static int __init init_impa7(void)
|
|||
if (impa7_mtd[i]) {
|
||||
impa7_mtd[i]->owner = THIS_MODULE;
|
||||
devicesfound++;
|
||||
mtd_device_parse_register(impa7_mtd[i], NULL, NULL,
|
||||
partitions,
|
||||
ARRAY_SIZE(partitions));
|
||||
mtd_device_register(impa7_mtd[i], partitions,
|
||||
ARRAY_SIZE(partitions));
|
||||
} else {
|
||||
iounmap((void __iomem *)impa7_map[i].virt);
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ static int vr_nor_init_partitions(struct vr_nor_mtd *p)
|
|||
{
|
||||
/* register the flash bank */
|
||||
/* partition the flash bank */
|
||||
return mtd_device_parse_register(p->info, NULL, NULL, NULL, 0);
|
||||
return mtd_device_register(p->info, NULL, 0);
|
||||
}
|
||||
|
||||
static void vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
|
||||
|
|
|
@ -197,9 +197,8 @@ static int latch_addr_flash_probe(struct platform_device *dev)
|
|||
}
|
||||
info->mtd->dev.parent = &dev->dev;
|
||||
|
||||
mtd_device_parse_register(info->mtd, NULL, NULL,
|
||||
latch_addr_data->parts,
|
||||
latch_addr_data->nr_parts);
|
||||
mtd_device_register(info->mtd, latch_addr_data->parts,
|
||||
latch_addr_data->nr_parts);
|
||||
return 0;
|
||||
|
||||
iounmap:
|
||||
|
|
|
@ -97,8 +97,7 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
|
|||
goto err_out;
|
||||
}
|
||||
info->mtd->dev.parent = &dev->dev;
|
||||
err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts,
|
||||
pdata->nr_parts);
|
||||
err = mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts);
|
||||
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
|
|
@ -59,9 +59,9 @@ static int __init init_soleng_maps(void)
|
|||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
printk(KERN_NOTICE "Solution Engine: Flash at 0x%08lx, EPROM at 0x%08lx\n",
|
||||
soleng_flash_map.phys & 0x1fffffff,
|
||||
soleng_eprom_map.phys & 0x1fffffff);
|
||||
printk(KERN_NOTICE "Solution Engine: Flash at 0x%pap, EPROM at 0x%pap\n",
|
||||
&soleng_flash_map.phys,
|
||||
&soleng_eprom_map.phys);
|
||||
flash_mtd->owner = THIS_MODULE;
|
||||
|
||||
eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map);
|
||||
|
|
|
@ -160,8 +160,12 @@ static ssize_t mtdchar_read(struct file *file, char __user *buf, size_t count,
|
|||
|
||||
pr_debug("MTD_read\n");
|
||||
|
||||
if (*ppos + count > mtd->size)
|
||||
count = mtd->size - *ppos;
|
||||
if (*ppos + count > mtd->size) {
|
||||
if (*ppos < mtd->size)
|
||||
count = mtd->size - *ppos;
|
||||
else
|
||||
count = 0;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
@ -246,7 +250,7 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c
|
|||
|
||||
pr_debug("MTD_write\n");
|
||||
|
||||
if (*ppos == mtd->size)
|
||||
if (*ppos >= mtd->size)
|
||||
return -ENOSPC;
|
||||
|
||||
if (*ppos + count > mtd->size)
|
||||
|
|
|
@ -1155,21 +1155,29 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
|
|||
{
|
||||
int ret_code;
|
||||
ops->retlen = ops->oobretlen = 0;
|
||||
if (!mtd->_read_oob)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret_code = mtd_check_oob_ops(mtd, from, ops);
|
||||
if (ret_code)
|
||||
return ret_code;
|
||||
|
||||
ledtrig_mtd_activity();
|
||||
|
||||
/* Check the validity of a potential fallback on mtd->_read */
|
||||
if (!mtd->_read_oob && (!mtd->_read || ops->oobbuf))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (mtd->_read_oob)
|
||||
ret_code = mtd->_read_oob(mtd, from, ops);
|
||||
else
|
||||
ret_code = mtd->_read(mtd, from, ops->len, &ops->retlen,
|
||||
ops->datbuf);
|
||||
|
||||
/*
|
||||
* In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
|
||||
* similar to mtd->_read(), returning a non-negative integer
|
||||
* representing max bitflips. In other cases, mtd->_read_oob() may
|
||||
* return -EUCLEAN. In all cases, perform similar logic to mtd_read().
|
||||
*/
|
||||
ret_code = mtd->_read_oob(mtd, from, ops);
|
||||
if (unlikely(ret_code < 0))
|
||||
return ret_code;
|
||||
if (mtd->ecc_strength == 0)
|
||||
|
@ -1184,8 +1192,7 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
int ret;
|
||||
|
||||
ops->retlen = ops->oobretlen = 0;
|
||||
if (!mtd->_write_oob)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
|
||||
|
@ -1194,7 +1201,16 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
return ret;
|
||||
|
||||
ledtrig_mtd_activity();
|
||||
return mtd->_write_oob(mtd, to, ops);
|
||||
|
||||
/* Check the validity of a potential fallback on mtd->_write */
|
||||
if (!mtd->_write_oob && (!mtd->_write || ops->oobbuf))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (mtd->_write_oob)
|
||||
return mtd->_write_oob(mtd, to, ops);
|
||||
else
|
||||
return mtd->_write(mtd, to, ops->len, &ops->retlen,
|
||||
ops->datbuf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_write_oob);
|
||||
|
||||
|
|
|
@ -322,22 +322,6 @@ static inline void free_partition(struct mtd_part *p)
|
|||
kfree(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* mtd_parse_part - parse MTD partition looking for subpartitions
|
||||
*
|
||||
* @slave: part that is supposed to be a container and should be parsed
|
||||
* @types: NULL-terminated array with names of partition parsers to try
|
||||
*
|
||||
* Some partitions are kind of containers with extra subpartitions (volumes).
|
||||
* There can be various formats of such containers. This function tries to use
|
||||
* specified parsers to analyze given partition and registers found
|
||||
* subpartitions on success.
|
||||
*/
|
||||
static int mtd_parse_part(struct mtd_part *slave, const char *const *types)
|
||||
{
|
||||
return parse_mtd_partitions(&slave->mtd, types, NULL);
|
||||
}
|
||||
|
||||
static struct mtd_part *allocate_partition(struct mtd_info *parent,
|
||||
const struct mtd_partition *part, int partno,
|
||||
uint64_t cur_offset)
|
||||
|
@ -735,8 +719,8 @@ int add_mtd_partitions(struct mtd_info *master,
|
|||
|
||||
add_mtd_device(&slave->mtd);
|
||||
mtd_add_partition_attrs(slave);
|
||||
if (parts[i].types)
|
||||
mtd_parse_part(slave, parts[i].types);
|
||||
/* Look for subpartitions */
|
||||
parse_mtd_partitions(&slave->mtd, parts[i].types, NULL);
|
||||
|
||||
cur_offset = slave->offset + slave->mtd.size;
|
||||
}
|
||||
|
@ -812,6 +796,12 @@ static const char * const default_mtd_part_types[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
/* Check DT only when looking for subpartitions. */
|
||||
static const char * const default_subpartition_types[] = {
|
||||
"ofpart",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int mtd_part_do_parse(struct mtd_part_parser *parser,
|
||||
struct mtd_info *master,
|
||||
struct mtd_partitions *pparts,
|
||||
|
@ -882,7 +872,9 @@ static int mtd_part_of_parse(struct mtd_info *master,
|
|||
const char *fixed = "fixed-partitions";
|
||||
int ret, err = 0;
|
||||
|
||||
np = of_get_child_by_name(mtd_get_of_node(master), "partitions");
|
||||
np = mtd_get_of_node(master);
|
||||
if (!mtd_is_partition(master))
|
||||
np = of_get_child_by_name(np, "partitions");
|
||||
of_property_for_each_string(np, "compatible", prop, compat) {
|
||||
parser = mtd_part_get_compatible_parser(compat);
|
||||
if (!parser)
|
||||
|
@ -945,7 +937,8 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
|
|||
int ret, err = 0;
|
||||
|
||||
if (!types)
|
||||
types = default_mtd_part_types;
|
||||
types = mtd_is_partition(master) ? default_subpartition_types :
|
||||
default_mtd_part_types;
|
||||
|
||||
for ( ; *types; types++) {
|
||||
/*
|
||||
|
|
|
@ -4,3 +4,4 @@ config MTD_NAND_CORE
|
|||
source "drivers/mtd/nand/onenand/Kconfig"
|
||||
|
||||
source "drivers/mtd/nand/raw/Kconfig"
|
||||
source "drivers/mtd/nand/spi/Kconfig"
|
||||
|
|
|
@ -5,3 +5,4 @@ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
|
|||
|
||||
obj-y += onenand/
|
||||
obj-y += raw/
|
||||
obj-y += spi/
|
||||
|
|
|
@ -66,9 +66,8 @@ static int generic_onenand_probe(struct platform_device *pdev)
|
|||
goto out_iounmap;
|
||||
}
|
||||
|
||||
err = mtd_device_parse_register(&info->mtd, NULL, NULL,
|
||||
pdata ? pdata->parts : NULL,
|
||||
pdata ? pdata->nr_parts : 0);
|
||||
err = mtd_device_register(&info->mtd, pdata ? pdata->parts : NULL,
|
||||
pdata ? pdata->nr_parts : 0);
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
|
|
|
@ -933,9 +933,8 @@ static int s3c_onenand_probe(struct platform_device *pdev)
|
|||
if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ)
|
||||
dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n");
|
||||
|
||||
err = mtd_device_parse_register(mtd, NULL, NULL,
|
||||
pdata ? pdata->parts : NULL,
|
||||
pdata ? pdata->nr_parts : 0);
|
||||
err = mtd_device_register(mtd, pdata ? pdata->parts : NULL,
|
||||
pdata ? pdata->nr_parts : 0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to parse partitions and register the MTD device\n");
|
||||
onenand_release(mtd);
|
||||
|
|
|
@ -44,12 +44,12 @@ config MTD_NAND_DENALI
|
|||
tristate
|
||||
|
||||
config MTD_NAND_DENALI_PCI
|
||||
tristate "Support Denali NAND controller on Intel Moorestown"
|
||||
tristate "Support Denali NAND controller on Intel Moorestown"
|
||||
select MTD_NAND_DENALI
|
||||
depends on PCI
|
||||
help
|
||||
Enable the driver for NAND flash on Intel Moorestown, using the
|
||||
Denali NAND controller core.
|
||||
help
|
||||
Enable the driver for NAND flash on Intel Moorestown, using the
|
||||
Denali NAND controller core.
|
||||
|
||||
config MTD_NAND_DENALI_DT
|
||||
tristate "Support Denali NAND controller as a DT device"
|
||||
|
@ -77,9 +77,10 @@ config MTD_NAND_AMS_DELTA
|
|||
|
||||
config MTD_NAND_OMAP2
|
||||
tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
|
||||
depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
|
||||
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
|
||||
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
|
||||
and Keystone platforms.
|
||||
|
||||
config MTD_NAND_OMAP_BCH
|
||||
|
@ -137,7 +138,7 @@ config MTD_NAND_NDFC
|
|||
depends on 4xx
|
||||
select MTD_NAND_ECC_SMC
|
||||
help
|
||||
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
||||
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
||||
|
||||
config MTD_NAND_S3C2410_CLKSTOP
|
||||
bool "Samsung S3C NAND IDLE clock stop"
|
||||
|
@ -152,6 +153,7 @@ config MTD_NAND_S3C2410_CLKSTOP
|
|||
config MTD_NAND_TANGO
|
||||
tristate "NAND Flash support for Tango chips"
|
||||
depends on ARCH_TANGO || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables the NAND Flash controller on Tango chips.
|
||||
|
||||
|
@ -168,40 +170,40 @@ config MTD_NAND_DISKONCHIP
|
|||
these devices.
|
||||
|
||||
config MTD_NAND_DISKONCHIP_PROBE_ADVANCED
|
||||
bool "Advanced detection options for DiskOnChip"
|
||||
depends on MTD_NAND_DISKONCHIP
|
||||
help
|
||||
This option allows you to specify nonstandard address at which to
|
||||
probe for a DiskOnChip, or to change the detection options. You
|
||||
are unlikely to need any of this unless you are using LinuxBIOS.
|
||||
Say 'N'.
|
||||
bool "Advanced detection options for DiskOnChip"
|
||||
depends on MTD_NAND_DISKONCHIP
|
||||
help
|
||||
This option allows you to specify nonstandard address at which to
|
||||
probe for a DiskOnChip, or to change the detection options. You
|
||||
are unlikely to need any of this unless you are using LinuxBIOS.
|
||||
Say 'N'.
|
||||
|
||||
config MTD_NAND_DISKONCHIP_PROBE_ADDRESS
|
||||
hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
|
||||
depends on MTD_NAND_DISKONCHIP
|
||||
default "0"
|
||||
---help---
|
||||
By default, the probe for DiskOnChip devices will look for a
|
||||
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
|
||||
This option allows you to specify a single address at which to probe
|
||||
for the device, which is useful if you have other devices in that
|
||||
range which get upset when they are probed.
|
||||
hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
|
||||
depends on MTD_NAND_DISKONCHIP
|
||||
default "0"
|
||||
help
|
||||
By default, the probe for DiskOnChip devices will look for a
|
||||
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
|
||||
This option allows you to specify a single address at which to probe
|
||||
for the device, which is useful if you have other devices in that
|
||||
range which get upset when they are probed.
|
||||
|
||||
(Note that on PowerPC, the normal probe will only check at
|
||||
0xE4000000.)
|
||||
(Note that on PowerPC, the normal probe will only check at
|
||||
0xE4000000.)
|
||||
|
||||
Normally, you should leave this set to zero, to allow the probe at
|
||||
the normal addresses.
|
||||
Normally, you should leave this set to zero, to allow the probe at
|
||||
the normal addresses.
|
||||
|
||||
config MTD_NAND_DISKONCHIP_PROBE_HIGH
|
||||
bool "Probe high addresses"
|
||||
depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED
|
||||
help
|
||||
By default, the probe for DiskOnChip devices will look for a
|
||||
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
|
||||
This option changes to make it probe between 0xFFFC8000 and
|
||||
0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
|
||||
useful to you. Say 'N'.
|
||||
bool "Probe high addresses"
|
||||
depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED
|
||||
help
|
||||
By default, the probe for DiskOnChip devices will look for a
|
||||
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
|
||||
This option changes to make it probe between 0xFFFC8000 and
|
||||
0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be
|
||||
useful to you. Say 'N'.
|
||||
|
||||
config MTD_NAND_DISKONCHIP_BBTWRITE
|
||||
bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
|
||||
|
@ -247,7 +249,8 @@ config MTD_NAND_DOCG4
|
|||
|
||||
config MTD_NAND_SHARPSL
|
||||
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
|
||||
depends on ARCH_PXA
|
||||
depends on ARCH_PXA || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
|
||||
config MTD_NAND_CAFE
|
||||
tristate "NAND support for OLPC CAFÉ chip"
|
||||
|
@ -274,7 +277,9 @@ config MTD_NAND_CS553X
|
|||
|
||||
config MTD_NAND_ATMEL
|
||||
tristate "Support for NAND Flash / SmartMedia on AT91"
|
||||
depends on ARCH_AT91
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select GENERIC_ALLOCATOR
|
||||
select MFD_ATMEL_SMC
|
||||
help
|
||||
Enables support for NAND Flash / Smart Media Card interface
|
||||
|
@ -294,7 +299,8 @@ config MTD_NAND_MARVELL
|
|||
|
||||
config MTD_NAND_SLC_LPC32XX
|
||||
tristate "NXP LPC32xx SLC Controller"
|
||||
depends on ARCH_LPC32XX
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell
|
||||
chips) NAND controller. This is the default for the PHYTEC 3250
|
||||
|
@ -305,7 +311,8 @@ config MTD_NAND_SLC_LPC32XX
|
|||
|
||||
config MTD_NAND_MLC_LPC32XX
|
||||
tristate "NXP LPC32xx MLC Controller"
|
||||
depends on ARCH_LPC32XX
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
|
||||
controller. This is the default for the WORK92105 controller
|
||||
|
@ -339,17 +346,18 @@ config MTD_NAND_NANDSIM
|
|||
MTD nand layer.
|
||||
|
||||
config MTD_NAND_GPMI_NAND
|
||||
tristate "GPMI NAND Flash Controller driver"
|
||||
depends on MTD_NAND && MXS_DMA
|
||||
help
|
||||
Enables NAND Flash support for IMX23, IMX28 or IMX6.
|
||||
The GPMI controller is very powerful, with the help of BCH
|
||||
module, it can do the hardware ECC. The GPMI supports several
|
||||
NAND flashs at the same time.
|
||||
tristate "GPMI NAND Flash Controller driver"
|
||||
depends on MXS_DMA
|
||||
help
|
||||
Enables NAND Flash support for IMX23, IMX28 or IMX6.
|
||||
The GPMI controller is very powerful, with the help of BCH
|
||||
module, it can do the hardware ECC. The GPMI supports several
|
||||
NAND flashs at the same time.
|
||||
|
||||
config MTD_NAND_BRCMNAND
|
||||
tristate "Broadcom STB NAND controller"
|
||||
depends on ARM || ARM64 || MIPS
|
||||
depends on ARM || ARM64 || MIPS || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables the Broadcom NAND controller driver. The controller was
|
||||
originally designed for Set-Top Box but is used on various BCM7xxx,
|
||||
|
@ -358,6 +366,7 @@ config MTD_NAND_BRCMNAND
|
|||
config MTD_NAND_BCM47XXNFLASH
|
||||
tristate "Support for NAND flash on BCM4706 BCMA bus"
|
||||
depends on BCMA_NFLASH
|
||||
depends on BCMA
|
||||
help
|
||||
BCMA bus can have various flash memories attached, they are
|
||||
registered by bcma as platform devices. This enables driver for
|
||||
|
@ -399,7 +408,8 @@ config MTD_NAND_FSL_ELBC
|
|||
|
||||
config MTD_NAND_FSL_IFC
|
||||
tristate "NAND support for Freescale IFC controller"
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select FSL_IFC
|
||||
select MEMORY
|
||||
help
|
||||
|
@ -437,7 +447,8 @@ config MTD_NAND_VF610_NFC
|
|||
|
||||
config MTD_NAND_MXC
|
||||
tristate "MXC NAND support"
|
||||
depends on ARCH_MXC
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the driver for the NAND flash controller on the
|
||||
MXC processors.
|
||||
|
@ -451,15 +462,17 @@ config MTD_NAND_SH_FLCTL
|
|||
for NAND Flash using FLCTL.
|
||||
|
||||
config MTD_NAND_DAVINCI
|
||||
tristate "Support NAND on DaVinci/Keystone SoC"
|
||||
depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF)
|
||||
help
|
||||
tristate "Support NAND on DaVinci/Keystone SoC"
|
||||
depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enable the driver for NAND flash chips on Texas Instruments
|
||||
DaVinci/Keystone processors.
|
||||
|
||||
config MTD_NAND_TXX9NDFMC
|
||||
tristate "NAND Flash support for TXx9 SoC"
|
||||
depends on SOC_TX4938 || SOC_TX4939
|
||||
depends on SOC_TX4938 || SOC_TX4939 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the NAND flash controller on the TXx9 SoCs.
|
||||
|
||||
|
@ -471,28 +484,31 @@ config MTD_NAND_SOCRATES
|
|||
|
||||
config MTD_NAND_NUC900
|
||||
tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
|
||||
depends on ARCH_W90X900
|
||||
depends on ARCH_W90X900 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables the driver for the NAND Flash on evaluation board based
|
||||
on w90p910 / NUC9xx.
|
||||
|
||||
config MTD_NAND_JZ4740
|
||||
tristate "Support for JZ4740 SoC NAND controller"
|
||||
depends on MACH_JZ4740
|
||||
depends on MACH_JZ4740 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash on JZ4740 SoC based boards.
|
||||
Enables support for NAND Flash on JZ4740 SoC based boards.
|
||||
|
||||
config MTD_NAND_JZ4780
|
||||
tristate "Support for NAND on JZ4780 SoC"
|
||||
depends on MACH_JZ4780 && JZ4780_NEMC
|
||||
depends on JZ4780_NEMC
|
||||
help
|
||||
Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
|
||||
based boards, using the BCH controller for hardware error correction.
|
||||
|
||||
config MTD_NAND_FSMC
|
||||
tristate "Support for NAND on ST Micros FSMC"
|
||||
depends on OF
|
||||
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
|
||||
depends on OF && HAS_IOMEM
|
||||
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 || \
|
||||
COMPILE_TEST
|
||||
help
|
||||
Enables support for NAND Flash chips on the ST Microelectronics
|
||||
Flexible Static Memory Controller (FSMC)
|
||||
|
@ -506,19 +522,22 @@ config MTD_NAND_XWAY
|
|||
|
||||
config MTD_NAND_SUNXI
|
||||
tristate "Support for NAND on Allwinner SoCs"
|
||||
depends on ARCH_SUNXI
|
||||
depends on ARCH_SUNXI || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND Flash chips on Allwinner SoCs.
|
||||
|
||||
config MTD_NAND_HISI504
|
||||
tristate "Support for NAND controller on Hisilicon SoC Hip04"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND controller on Hisilicon SoC Hip04.
|
||||
|
||||
config MTD_NAND_QCOM
|
||||
tristate "Support for NAND on QCOM SoCs"
|
||||
depends on ARCH_QCOM
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
|
||||
controller. This controller is found on IPQ806x SoC.
|
||||
|
@ -526,8 +545,20 @@ config MTD_NAND_QCOM
|
|||
config MTD_NAND_MTK
|
||||
tristate "Support for NAND controller on MTK SoCs"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND controller on MTK SoCs.
|
||||
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
|
||||
|
||||
config MTD_NAND_TEGRA
|
||||
tristate "Support for NAND controller on NVIDIA Tegra"
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enables support for NAND flash controller on NVIDIA Tegra SoC.
|
||||
The driver has been developed and tested on a Tegra 2 SoC. DMA
|
||||
support, raw read/write page as well as HW ECC read/write page
|
||||
is supported. Extra OOB bytes when using HW ECC are currently
|
||||
not supported.
|
||||
|
||||
endif # MTD_NAND
|
||||
|
|
|
@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
|
|||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
|
||||
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
|
||||
obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
|
||||
|
||||
nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
|
||||
nand-objs += nand_amd.o
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
@ -129,6 +128,11 @@
|
|||
#define DEFAULT_TIMEOUT_MS 1000
|
||||
#define MIN_DMA_LEN 128
|
||||
|
||||
static bool atmel_nand_avoid_dma __read_mostly;
|
||||
|
||||
MODULE_PARM_DESC(avoiddma, "Avoid using DMA");
|
||||
module_param_named(avoiddma, atmel_nand_avoid_dma, bool, 0400);
|
||||
|
||||
enum atmel_nand_rb_type {
|
||||
ATMEL_NAND_NO_RB,
|
||||
ATMEL_NAND_NATIVE_RB,
|
||||
|
@ -197,7 +201,7 @@ struct atmel_nand_controller_ops {
|
|||
int (*remove)(struct atmel_nand_controller *nc);
|
||||
void (*nand_init)(struct atmel_nand_controller *nc,
|
||||
struct atmel_nand *nand);
|
||||
int (*ecc_init)(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);
|
||||
};
|
||||
|
@ -211,7 +215,7 @@ struct atmel_nand_controller_caps {
|
|||
};
|
||||
|
||||
struct atmel_nand_controller {
|
||||
struct nand_hw_control base;
|
||||
struct nand_controller base;
|
||||
const struct atmel_nand_controller_caps *caps;
|
||||
struct device *dev;
|
||||
struct regmap *smc;
|
||||
|
@ -222,7 +226,7 @@ struct atmel_nand_controller {
|
|||
};
|
||||
|
||||
static inline struct atmel_nand_controller *
|
||||
to_nand_controller(struct nand_hw_control *ctl)
|
||||
to_nand_controller(struct nand_controller *ctl)
|
||||
{
|
||||
return container_of(ctl, struct atmel_nand_controller, base);
|
||||
}
|
||||
|
@ -234,7 +238,7 @@ struct atmel_smc_nand_controller {
|
|||
};
|
||||
|
||||
static inline struct atmel_smc_nand_controller *
|
||||
to_smc_nand_controller(struct nand_hw_control *ctl)
|
||||
to_smc_nand_controller(struct nand_controller *ctl)
|
||||
{
|
||||
return container_of(to_nand_controller(ctl),
|
||||
struct atmel_smc_nand_controller, base);
|
||||
|
@ -258,7 +262,7 @@ struct atmel_hsmc_nand_controller {
|
|||
};
|
||||
|
||||
static inline struct atmel_hsmc_nand_controller *
|
||||
to_hsmc_nand_controller(struct nand_hw_control *ctl)
|
||||
to_hsmc_nand_controller(struct nand_controller *ctl)
|
||||
{
|
||||
return container_of(to_nand_controller(ctl),
|
||||
struct atmel_hsmc_nand_controller, base);
|
||||
|
@ -1128,9 +1132,8 @@ static int atmel_nand_pmecc_init(struct nand_chip *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_nand_ecc_init(struct atmel_nand *nand)
|
||||
static int atmel_nand_ecc_init(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = &nand->base;
|
||||
struct atmel_nand_controller *nc;
|
||||
int ret;
|
||||
|
||||
|
@ -1165,12 +1168,11 @@ static int atmel_nand_ecc_init(struct atmel_nand *nand)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand)
|
||||
static int atmel_hsmc_nand_ecc_init(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = &nand->base;
|
||||
int ret;
|
||||
|
||||
ret = atmel_nand_ecc_init(nand);
|
||||
ret = atmel_nand_ecc_init(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1553,23 +1555,7 @@ static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc,
|
|||
chip->select_chip = atmel_hsmc_nand_select_chip;
|
||||
}
|
||||
|
||||
static int atmel_nand_detect(struct atmel_nand *nand)
|
||||
{
|
||||
struct nand_chip *chip = &nand->base;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct atmel_nand_controller *nc;
|
||||
int ret;
|
||||
|
||||
nc = to_nand_controller(chip->controller);
|
||||
|
||||
ret = nand_scan_ident(mtd, nand->numcs, NULL);
|
||||
if (ret)
|
||||
dev_err(nc->dev, "nand_scan_ident() failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_nand_unregister(struct atmel_nand *nand)
|
||||
static int atmel_nand_controller_remove_nand(struct atmel_nand *nand)
|
||||
{
|
||||
struct nand_chip *chip = &nand->base;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
@ -1585,60 +1571,6 @@ static int atmel_nand_unregister(struct atmel_nand *nand)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_nand_register(struct atmel_nand *nand)
|
||||
{
|
||||
struct nand_chip *chip = &nand->base;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct atmel_nand_controller *nc;
|
||||
int ret;
|
||||
|
||||
nc = to_nand_controller(chip->controller);
|
||||
|
||||
if (nc->caps->legacy_of_bindings || !nc->dev->of_node) {
|
||||
/*
|
||||
* We keep the MTD name unchanged to avoid breaking platforms
|
||||
* where the MTD cmdline parser is used and the bootloader
|
||||
* has not been updated to use the new naming scheme.
|
||||
*/
|
||||
mtd->name = "atmel_nand";
|
||||
} else if (!mtd->name) {
|
||||
/*
|
||||
* If the new bindings are used and the bootloader has not been
|
||||
* updated to pass a new mtdparts parameter on the cmdline, you
|
||||
* should define the following property in your nand node:
|
||||
*
|
||||
* label = "atmel_nand";
|
||||
*
|
||||
* This way, mtd->name will be set by the core when
|
||||
* nand_set_flash_node() is called.
|
||||
*/
|
||||
mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL,
|
||||
"%s:nand.%d", dev_name(nc->dev),
|
||||
nand->cs[0].id);
|
||||
if (!mtd->name) {
|
||||
dev_err(nc->dev, "Failed to allocate mtd->name\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret) {
|
||||
dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(nc->dev, "Failed to register mtd device: %d\n", ret);
|
||||
nand_cleanup(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_add_tail(&nand->node, &nc->chips);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
|
||||
struct device_node *np,
|
||||
int reg_cells)
|
||||
|
@ -1750,6 +1682,8 @@ static int
|
|||
atmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
|
||||
struct atmel_nand *nand)
|
||||
{
|
||||
struct nand_chip *chip = &nand->base;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
|
||||
/* No card inserted, skip this NAND. */
|
||||
|
@ -1760,15 +1694,22 @@ atmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
|
|||
|
||||
nc->caps->ops->nand_init(nc, nand);
|
||||
|
||||
ret = atmel_nand_detect(nand);
|
||||
if (ret)
|
||||
ret = nand_scan(mtd, nand->numcs);
|
||||
if (ret) {
|
||||
dev_err(nc->dev, "NAND scan failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nc->caps->ops->ecc_init(nand);
|
||||
if (ret)
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(nc->dev, "Failed to register mtd device: %d\n", ret);
|
||||
nand_cleanup(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return atmel_nand_register(nand);
|
||||
list_add_tail(&nand->node, &nc->chips);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1778,7 +1719,7 @@ atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc)
|
|||
int ret;
|
||||
|
||||
list_for_each_entry_safe(nand, tmp, &nc->chips, node) {
|
||||
ret = atmel_nand_unregister(nand);
|
||||
ret = atmel_nand_controller_remove_nand(nand);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -1953,6 +1894,51 @@ static const struct of_device_id atmel_matrix_of_ids[] = {
|
|||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static int atmel_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct atmel_nand_controller *nc = to_nand_controller(chip->controller);
|
||||
struct atmel_nand *nand = to_atmel_nand(chip);
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
|
||||
ret = nc->caps->ops->ecc_init(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nc->caps->legacy_of_bindings || !nc->dev->of_node) {
|
||||
/*
|
||||
* We keep the MTD name unchanged to avoid breaking platforms
|
||||
* where the MTD cmdline parser is used and the bootloader
|
||||
* has not been updated to use the new naming scheme.
|
||||
*/
|
||||
mtd->name = "atmel_nand";
|
||||
} else if (!mtd->name) {
|
||||
/*
|
||||
* If the new bindings are used and the bootloader has not been
|
||||
* updated to pass a new mtdparts parameter on the cmdline, you
|
||||
* should define the following property in your nand node:
|
||||
*
|
||||
* label = "atmel_nand";
|
||||
*
|
||||
* This way, mtd->name will be set by the core when
|
||||
* nand_set_flash_node() is called.
|
||||
*/
|
||||
mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL,
|
||||
"%s:nand.%d", dev_name(nc->dev),
|
||||
nand->cs[0].id);
|
||||
if (!mtd->name) {
|
||||
dev_err(nc->dev, "Failed to allocate mtd->name\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops atmel_nand_controller_ops = {
|
||||
.attach_chip = atmel_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
|
||||
struct platform_device *pdev,
|
||||
const struct atmel_nand_controller_caps *caps)
|
||||
|
@ -1961,7 +1947,8 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
|
|||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
nand_hw_control_init(&nc->base);
|
||||
nand_controller_init(&nc->base);
|
||||
nc->base.ops = &atmel_nand_controller_ops;
|
||||
INIT_LIST_HEAD(&nc->chips);
|
||||
nc->dev = dev;
|
||||
nc->caps = caps;
|
||||
|
@ -1977,7 +1964,7 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (nc->caps->has_dma) {
|
||||
if (nc->caps->has_dma && !atmel_nand_avoid_dma) {
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
|
@ -2045,7 +2032,7 @@ atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
|
|||
return ret;
|
||||
}
|
||||
|
||||
nc->ebi_csa_offs = (unsigned int)match->data;
|
||||
nc->ebi_csa_offs = (uintptr_t)match->data;
|
||||
|
||||
/*
|
||||
* The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
|
||||
|
@ -2214,9 +2201,9 @@ atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
nc->sram.virt = gen_pool_dma_alloc(nc->sram.pool,
|
||||
ATMEL_NFC_SRAM_SIZE,
|
||||
&nc->sram.dma);
|
||||
nc->sram.virt = (void __iomem *)gen_pool_dma_alloc(nc->sram.pool,
|
||||
ATMEL_NFC_SRAM_SIZE,
|
||||
&nc->sram.dma);
|
||||
if (!nc->sram.virt) {
|
||||
dev_err(nc->base.dev,
|
||||
"Could not allocate memory from the NFC SRAM pool\n");
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
|
|
@ -114,7 +114,7 @@ enum {
|
|||
|
||||
struct brcmnand_controller {
|
||||
struct device *dev;
|
||||
struct nand_hw_control controller;
|
||||
struct nand_controller controller;
|
||||
void __iomem *nand_base;
|
||||
void __iomem *nand_fc; /* flash cache */
|
||||
void __iomem *flash_dma_base;
|
||||
|
@ -2208,6 +2208,40 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int brcmnand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct brcmnand_host *host = nand_get_controller_data(chip);
|
||||
int ret;
|
||||
|
||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
/*
|
||||
* Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
|
||||
* to/from, and have nand_base pass us a bounce buffer instead, as
|
||||
* needed.
|
||||
*/
|
||||
chip->options |= NAND_USE_BOUNCE_BUFFER;
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
if (brcmnand_setup_dev(host))
|
||||
return -ENXIO;
|
||||
|
||||
chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
|
||||
|
||||
/* only use our internal HW threshold */
|
||||
mtd->bitflip_threshold = 1;
|
||||
|
||||
ret = brcmstb_choose_ecc_layout(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops brcmnand_controller_ops = {
|
||||
.attach_chip = brcmnand_attach_chip,
|
||||
};
|
||||
|
||||
static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
|
||||
{
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
|
@ -2267,33 +2301,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
|
|||
nand_writereg(ctrl, cfg_offs,
|
||||
nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
|
||||
|
||||
ret = nand_scan_ident(mtd, 1, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
/*
|
||||
* Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
|
||||
* to/from, and have nand_base pass us a bounce buffer instead, as
|
||||
* needed.
|
||||
*/
|
||||
chip->options |= NAND_USE_BOUNCE_BUFFER;
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
if (brcmnand_setup_dev(host))
|
||||
return -ENXIO;
|
||||
|
||||
chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
|
||||
/* only use our internal HW threshold */
|
||||
mtd->bitflip_threshold = 1;
|
||||
|
||||
ret = brcmstb_choose_ecc_layout(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
ret = nand_scan(mtd, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -2433,7 +2441,8 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|||
|
||||
init_completion(&ctrl->done);
|
||||
init_completion(&ctrl->dma_done);
|
||||
nand_hw_control_init(&ctrl->controller);
|
||||
nand_controller_init(&ctrl->controller);
|
||||
ctrl->controller.ops = &brcmnand_controller_ops;
|
||||
INIT_LIST_HEAD(&ctrl->host_list);
|
||||
|
||||
/* NAND register range */
|
||||
|
|
|
@ -67,6 +67,7 @@ struct cafe_priv {
|
|||
int nr_data;
|
||||
int data_pos;
|
||||
int page_addr;
|
||||
bool usedma;
|
||||
dma_addr_t dmaaddr;
|
||||
unsigned char *dmabuf;
|
||||
};
|
||||
|
@ -121,7 +122,7 @@ static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
|||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct cafe_priv *cafe = nand_get_controller_data(chip);
|
||||
|
||||
if (usedma)
|
||||
if (cafe->usedma)
|
||||
memcpy(cafe->dmabuf + cafe->datalen, buf, len);
|
||||
else
|
||||
memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len);
|
||||
|
@ -137,7 +138,7 @@ static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct cafe_priv *cafe = nand_get_controller_data(chip);
|
||||
|
||||
if (usedma)
|
||||
if (cafe->usedma)
|
||||
memcpy(buf, cafe->dmabuf + cafe->datalen, len);
|
||||
else
|
||||
memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len);
|
||||
|
@ -253,7 +254,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
|||
/* NB: The datasheet lies -- we really should be subtracting 1 here */
|
||||
cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN);
|
||||
cafe_writel(cafe, 0x90000000, NAND_IRQ);
|
||||
if (usedma && (ctl1 & (3<<25))) {
|
||||
if (cafe->usedma && (ctl1 & (3<<25))) {
|
||||
uint32_t dmactl = 0xc0000000 + cafe->datalen;
|
||||
/* If WR or RD bits set, set up DMA */
|
||||
if (ctl1 & (1<<26)) {
|
||||
|
@ -345,11 +346,6 @@ static irqreturn_t cafe_nand_interrupt(int irq, void *id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void cafe_nand_bug(struct mtd_info *mtd)
|
||||
{
|
||||
BUG();
|
||||
}
|
||||
|
||||
static int cafe_nand_write_oob(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, int page)
|
||||
{
|
||||
|
@ -598,6 +594,76 @@ static int cafe_mul(int x)
|
|||
return gf4096_mul(x, 0xe01);
|
||||
}
|
||||
|
||||
static int cafe_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct cafe_priv *cafe = nand_get_controller_data(chip);
|
||||
int err = 0;
|
||||
|
||||
cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112,
|
||||
&cafe->dmaaddr, GFP_KERNEL);
|
||||
if (!cafe->dmabuf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set up DMA address */
|
||||
cafe_writel(cafe, lower_32_bits(cafe->dmaaddr), NAND_DMA_ADDR0);
|
||||
cafe_writel(cafe, upper_32_bits(cafe->dmaaddr), NAND_DMA_ADDR1);
|
||||
|
||||
cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
|
||||
cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
|
||||
|
||||
/* Restore the DMA flag */
|
||||
cafe->usedma = usedma;
|
||||
|
||||
cafe->ctl2 = BIT(27); /* Reed-Solomon ECC */
|
||||
if (mtd->writesize == 2048)
|
||||
cafe->ctl2 |= BIT(29); /* 2KiB page size */
|
||||
|
||||
/* Set up ECC according to the type of chip we found */
|
||||
mtd_set_ooblayout(mtd, &cafe_ooblayout_ops);
|
||||
if (mtd->writesize == 2048) {
|
||||
cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
|
||||
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
|
||||
} else if (mtd->writesize == 512) {
|
||||
cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
|
||||
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
|
||||
} else {
|
||||
dev_warn(&cafe->pdev->dev,
|
||||
"Unexpected NAND flash writesize %d. Aborting\n",
|
||||
mtd->writesize);
|
||||
err = -ENOTSUPP;
|
||||
goto out_free_dma;
|
||||
}
|
||||
|
||||
cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
cafe->nand.ecc.size = mtd->writesize;
|
||||
cafe->nand.ecc.bytes = 14;
|
||||
cafe->nand.ecc.strength = 4;
|
||||
cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel;
|
||||
cafe->nand.ecc.write_oob = cafe_nand_write_oob;
|
||||
cafe->nand.ecc.read_page = cafe_nand_read_page;
|
||||
cafe->nand.ecc.read_oob = cafe_nand_read_oob;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_dma:
|
||||
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void cafe_nand_detach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct cafe_priv *cafe = nand_get_controller_data(chip);
|
||||
|
||||
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops cafe_nand_controller_ops = {
|
||||
.attach_chip = cafe_nand_attach_chip,
|
||||
.detach_chip = cafe_nand_detach_chip,
|
||||
};
|
||||
|
||||
static int cafe_nand_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
|
@ -605,7 +671,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
struct cafe_priv *cafe;
|
||||
uint32_t ctrl;
|
||||
int err = 0;
|
||||
int old_dma;
|
||||
|
||||
/* Very old versions shared the same PCI ident for all three
|
||||
functions on the chip. Verify the class too... */
|
||||
|
@ -713,65 +778,15 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
cafe_readl(cafe, GLOBAL_CTRL),
|
||||
cafe_readl(cafe, GLOBAL_IRQ_MASK));
|
||||
|
||||
/* Do not use the DMA for the nand_scan_ident() */
|
||||
old_dma = usedma;
|
||||
usedma = 0;
|
||||
/* Do not use the DMA during the NAND identification */
|
||||
cafe->usedma = 0;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
err = nand_scan_ident(mtd, 2, NULL);
|
||||
cafe->nand.dummy_controller.ops = &cafe_nand_controller_ops;
|
||||
err = nand_scan(mtd, 2);
|
||||
if (err)
|
||||
goto out_irq;
|
||||
|
||||
cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112,
|
||||
&cafe->dmaaddr, GFP_KERNEL);
|
||||
if (!cafe->dmabuf) {
|
||||
err = -ENOMEM;
|
||||
goto out_irq;
|
||||
}
|
||||
|
||||
/* Set up DMA address */
|
||||
cafe_writel(cafe, lower_32_bits(cafe->dmaaddr), NAND_DMA_ADDR0);
|
||||
cafe_writel(cafe, upper_32_bits(cafe->dmaaddr), NAND_DMA_ADDR1);
|
||||
|
||||
cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
|
||||
cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
|
||||
|
||||
/* Restore the DMA flag */
|
||||
usedma = old_dma;
|
||||
|
||||
cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
|
||||
if (mtd->writesize == 2048)
|
||||
cafe->ctl2 |= 1<<29; /* 2KiB page size */
|
||||
|
||||
/* Set up ECC according to the type of chip we found */
|
||||
mtd_set_ooblayout(mtd, &cafe_ooblayout_ops);
|
||||
if (mtd->writesize == 2048) {
|
||||
cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
|
||||
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
|
||||
} else if (mtd->writesize == 512) {
|
||||
cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
|
||||
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
|
||||
} else {
|
||||
pr_warn("Unexpected NAND flash writesize %d. Aborting\n",
|
||||
mtd->writesize);
|
||||
goto out_free_dma;
|
||||
}
|
||||
cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
cafe->nand.ecc.size = mtd->writesize;
|
||||
cafe->nand.ecc.bytes = 14;
|
||||
cafe->nand.ecc.strength = 4;
|
||||
cafe->nand.ecc.hwctl = (void *)cafe_nand_bug;
|
||||
cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
|
||||
cafe->nand.ecc.correct = (void *)cafe_nand_bug;
|
||||
cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel;
|
||||
cafe->nand.ecc.write_oob = cafe_nand_write_oob;
|
||||
cafe->nand.ecc.read_page = cafe_nand_read_page;
|
||||
cafe->nand.ecc.read_oob = cafe_nand_read_oob;
|
||||
|
||||
err = nand_scan_tail(mtd);
|
||||
if (err)
|
||||
goto out_free_dma;
|
||||
|
||||
pci_set_drvdata(pdev, mtd);
|
||||
|
||||
mtd->name = "cafe_nand";
|
||||
|
@ -783,8 +798,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
|
||||
out_cleanup_nand:
|
||||
nand_cleanup(&cafe->nand);
|
||||
out_free_dma:
|
||||
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
||||
out_irq:
|
||||
/* Disable NAND IRQ in global IRQ mask register */
|
||||
cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
|
||||
|
|
|
@ -200,8 +200,8 @@ static int __init cmx270_init(void)
|
|||
}
|
||||
|
||||
/* Register the partitions */
|
||||
ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL,
|
||||
partition_info, NUM_PARTITIONS);
|
||||
ret = mtd_device_register(cmx270_nand_mtd, partition_info,
|
||||
NUM_PARTITIONS);
|
||||
if (ret)
|
||||
goto err_scan;
|
||||
|
||||
|
|
|
@ -310,8 +310,7 @@ static int __init cs553x_init(void)
|
|||
for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
|
||||
if (cs553x_mtd[i]) {
|
||||
/* If any devices registered, return success. Else the last error. */
|
||||
mtd_device_parse_register(cs553x_mtd[i], NULL, NULL,
|
||||
NULL, 0);
|
||||
mtd_device_register(cs553x_mtd[i], NULL, 0);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,15 +53,14 @@
|
|||
struct davinci_nand_info {
|
||||
struct nand_chip chip;
|
||||
|
||||
struct device *dev;
|
||||
struct platform_device *pdev;
|
||||
|
||||
bool is_readmode;
|
||||
|
||||
void __iomem *base;
|
||||
void __iomem *vaddr;
|
||||
|
||||
uint32_t ioaddr;
|
||||
uint32_t current_cs;
|
||||
void __iomem *current_cs;
|
||||
|
||||
uint32_t mask_chipsel;
|
||||
uint32_t mask_ale;
|
||||
|
@ -102,17 +101,17 @@ static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
|
|||
unsigned int ctrl)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
uint32_t addr = info->current_cs;
|
||||
void __iomem *addr = info->current_cs;
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
|
||||
/* Did the control lines change? */
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE)
|
||||
addr |= info->mask_cle;
|
||||
addr += info->mask_cle;
|
||||
else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE)
|
||||
addr |= info->mask_ale;
|
||||
addr += info->mask_ale;
|
||||
|
||||
nand->IO_ADDR_W = (void __iomem __force *)addr;
|
||||
nand->IO_ADDR_W = addr;
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
|
@ -122,14 +121,14 @@ static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
|
|||
static void nand_davinci_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
uint32_t addr = info->ioaddr;
|
||||
|
||||
info->current_cs = info->vaddr;
|
||||
|
||||
/* maybe kick in a second chipselect */
|
||||
if (chip > 0)
|
||||
addr |= info->mask_chipsel;
|
||||
info->current_cs = addr;
|
||||
info->current_cs += info->mask_chipsel;
|
||||
|
||||
info->chip.IO_ADDR_W = (void __iomem __force *)addr;
|
||||
info->chip.IO_ADDR_W = info->current_cs;
|
||||
info->chip.IO_ADDR_R = info->chip.IO_ADDR_W;
|
||||
}
|
||||
|
||||
|
@ -319,7 +318,7 @@ static int nand_davinci_correct_4bit(struct mtd_info *mtd,
|
|||
/* Unpack ten bytes into eight 10 bit values. We know we're
|
||||
* little-endian, and use type punning for less shifting/masking.
|
||||
*/
|
||||
if (WARN_ON(0x01 & (unsigned) ecc_code))
|
||||
if (WARN_ON(0x01 & (uintptr_t)ecc_code))
|
||||
return -EINVAL;
|
||||
ecc16 = (unsigned short *)ecc_code;
|
||||
|
||||
|
@ -441,9 +440,9 @@ static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
|
||||
if ((0x03 & ((uintptr_t)buf)) == 0 && (0x03 & len) == 0)
|
||||
ioread32_rep(chip->IO_ADDR_R, buf, len >> 2);
|
||||
else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
|
||||
else if ((0x01 & ((uintptr_t)buf)) == 0 && (0x01 & len) == 0)
|
||||
ioread16_rep(chip->IO_ADDR_R, buf, len >> 1);
|
||||
else
|
||||
ioread8_rep(chip->IO_ADDR_R, buf, len);
|
||||
|
@ -454,9 +453,9 @@ static void nand_davinci_write_buf(struct mtd_info *mtd,
|
|||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
|
||||
if ((0x03 & ((uintptr_t)buf)) == 0 && (0x03 & len) == 0)
|
||||
iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2);
|
||||
else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
|
||||
else if ((0x01 & ((uintptr_t)buf)) == 0 && (0x01 & len) == 0)
|
||||
iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1);
|
||||
else
|
||||
iowrite8_rep(chip->IO_ADDR_R, buf, len);
|
||||
|
@ -606,6 +605,104 @@ static struct davinci_nand_pdata
|
|||
}
|
||||
#endif
|
||||
|
||||
static int davinci_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
struct davinci_nand_pdata *pdata = nand_davinci_get_pdata(info->pdev);
|
||||
int ret = 0;
|
||||
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
|
||||
switch (info->chip.ecc.mode) {
|
||||
case NAND_ECC_NONE:
|
||||
pdata->ecc_bits = 0;
|
||||
break;
|
||||
case NAND_ECC_SOFT:
|
||||
pdata->ecc_bits = 0;
|
||||
/*
|
||||
* This driver expects Hamming based ECC when ecc_mode is set
|
||||
* to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
|
||||
* avoid adding an extra ->ecc_algo field to
|
||||
* davinci_nand_pdata.
|
||||
*/
|
||||
info->chip.ecc.algo = NAND_ECC_HAMMING;
|
||||
break;
|
||||
case NAND_ECC_HW:
|
||||
if (pdata->ecc_bits == 4) {
|
||||
/*
|
||||
* No sanity checks: CPUs must support this,
|
||||
* and the chips may not use NAND_BUSWIDTH_16.
|
||||
*/
|
||||
|
||||
/* No sharing 4-bit hardware between chipselects yet */
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (ecc4_busy)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ecc4_busy = true;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
if (ret == -EBUSY)
|
||||
return ret;
|
||||
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_4bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_4bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
|
||||
info->chip.ecc.bytes = 10;
|
||||
info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
|
||||
info->chip.ecc.algo = NAND_ECC_BCH;
|
||||
} else {
|
||||
/* 1bit ecc hamming */
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_1bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
|
||||
info->chip.ecc.bytes = 3;
|
||||
info->chip.ecc.algo = NAND_ECC_HAMMING;
|
||||
}
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.strength = pdata->ecc_bits;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update ECC layout if needed ... for 1-bit HW ECC, the default
|
||||
* is OK, but it allocates 6 bytes when only 3 are needed (for
|
||||
* each 512 bytes). For the 4-bit HW ECC, that default is not
|
||||
* usable: 10 bytes are needed, not 6.
|
||||
*/
|
||||
if (pdata->ecc_bits == 4) {
|
||||
int chunks = mtd->writesize / 512;
|
||||
|
||||
if (!chunks || mtd->oobsize < 16) {
|
||||
dev_dbg(&info->pdev->dev, "too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* For small page chips, preserve the manufacturer's
|
||||
* badblock marking data ... and make sure a flash BBT
|
||||
* table marker fits in the free bytes.
|
||||
*/
|
||||
if (chunks == 1) {
|
||||
mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops);
|
||||
} else if (chunks == 4 || chunks == 8) {
|
||||
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
|
||||
info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
|
||||
} else {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops davinci_nand_controller_ops = {
|
||||
.attach_chip = davinci_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int nand_davinci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_nand_pdata *pdata;
|
||||
|
@ -659,7 +756,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
|
|||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
info->pdev = pdev;
|
||||
info->base = base;
|
||||
info->vaddr = vaddr;
|
||||
|
||||
|
@ -680,9 +777,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
|
|||
info->chip.bbt_md = pdata->bbt_md;
|
||||
info->timing = pdata->timing;
|
||||
|
||||
info->ioaddr = (uint32_t __force) vaddr;
|
||||
|
||||
info->current_cs = info->ioaddr;
|
||||
info->current_cs = info->vaddr;
|
||||
info->core_chipsel = pdata->core_chipsel;
|
||||
info->mask_chipsel = pdata->mask_chipsel;
|
||||
|
||||
|
@ -711,100 +806,15 @@ static int nand_davinci_probe(struct platform_device *pdev)
|
|||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
/* Scan to find existence of the device(s) */
|
||||
ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL);
|
||||
info->chip.dummy_controller.ops = &davinci_nand_controller_ops;
|
||||
ret = nand_scan(mtd, pdata->mask_chipsel ? 2 : 1);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (info->chip.ecc.mode) {
|
||||
case NAND_ECC_NONE:
|
||||
pdata->ecc_bits = 0;
|
||||
break;
|
||||
case NAND_ECC_SOFT:
|
||||
pdata->ecc_bits = 0;
|
||||
/*
|
||||
* This driver expects Hamming based ECC when ecc_mode is set
|
||||
* to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
|
||||
* avoid adding an extra ->ecc_algo field to
|
||||
* davinci_nand_pdata.
|
||||
*/
|
||||
info->chip.ecc.algo = NAND_ECC_HAMMING;
|
||||
break;
|
||||
case NAND_ECC_HW:
|
||||
if (pdata->ecc_bits == 4) {
|
||||
/* No sanity checks: CPUs must support this,
|
||||
* and the chips may not use NAND_BUSWIDTH_16.
|
||||
*/
|
||||
|
||||
/* No sharing 4-bit hardware between chipselects yet */
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (ecc4_busy)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ecc4_busy = true;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
if (ret == -EBUSY)
|
||||
return ret;
|
||||
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_4bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_4bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
|
||||
info->chip.ecc.bytes = 10;
|
||||
info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
|
||||
info->chip.ecc.algo = NAND_ECC_BCH;
|
||||
} else {
|
||||
/* 1bit ecc hamming */
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_1bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
|
||||
info->chip.ecc.bytes = 3;
|
||||
info->chip.ecc.algo = NAND_ECC_HAMMING;
|
||||
}
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.strength = pdata->ecc_bits;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Update ECC layout if needed ... for 1-bit HW ECC, the default
|
||||
* is OK, but it allocates 6 bytes when only 3 are needed (for
|
||||
* each 512 bytes). For the 4-bit HW ECC, that default is not
|
||||
* usable: 10 bytes are needed, not 6.
|
||||
*/
|
||||
if (pdata->ecc_bits == 4) {
|
||||
int chunks = mtd->writesize / 512;
|
||||
|
||||
if (!chunks || mtd->oobsize < 16) {
|
||||
dev_dbg(&pdev->dev, "too small\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* For small page chips, preserve the manufacturer's
|
||||
* badblock marking data ... and make sure a flash BBT
|
||||
* table marker fits in the free bytes.
|
||||
*/
|
||||
if (chunks == 1) {
|
||||
mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops);
|
||||
} else if (chunks == 4 || chunks == 8) {
|
||||
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
|
||||
info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
|
||||
} else {
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (pdata->parts)
|
||||
ret = mtd_device_parse_register(mtd, NULL, NULL,
|
||||
pdata->parts, pdata->nr_parts);
|
||||
ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
|
||||
else
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret < 0)
|
||||
|
@ -819,11 +829,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
|
|||
err_cleanup_nand:
|
||||
nand_cleanup(&info->chip);
|
||||
|
||||
err:
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
|
||||
ecc4_busy = false;
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,14 +51,6 @@ MODULE_LICENSE("GPL");
|
|||
#define DENALI_INVALID_BANK -1
|
||||
#define DENALI_NR_BANKS 4
|
||||
|
||||
/*
|
||||
* The bus interface clock, clk_x, is phase aligned with the core clock. The
|
||||
* clk_x is an integral multiple N of the core clk. The value N is configured
|
||||
* at IP delivery time, and its available value is 4, 5, or 6. We need to align
|
||||
* to the largest value to make it work with any possible configuration.
|
||||
*/
|
||||
#define DENALI_CLK_X_MULT 6
|
||||
|
||||
static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
|
||||
{
|
||||
return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
|
||||
|
@ -954,7 +946,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
{
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
const struct nand_sdr_timings *timings;
|
||||
unsigned long t_clk;
|
||||
unsigned long t_x, mult_x;
|
||||
int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
|
||||
int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
|
||||
int addr_2_data_mask;
|
||||
|
@ -965,15 +957,24 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
return PTR_ERR(timings);
|
||||
|
||||
/* clk_x period in picoseconds */
|
||||
t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
|
||||
if (!t_clk)
|
||||
t_x = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
|
||||
if (!t_x)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The bus interface clock, clk_x, is phase aligned with the core clock.
|
||||
* The clk_x is an integral multiple N of the core clk. The value N is
|
||||
* configured at IP delivery time, and its available value is 4, 5, 6.
|
||||
*/
|
||||
mult_x = DIV_ROUND_CLOSEST_ULL(denali->clk_x_rate, denali->clk_rate);
|
||||
if (mult_x < 4 || mult_x > 6)
|
||||
return -EINVAL;
|
||||
|
||||
if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
|
||||
return 0;
|
||||
|
||||
/* tREA -> ACC_CLKS */
|
||||
acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
|
||||
acc_clks = DIV_ROUND_UP(timings->tREA_max, t_x);
|
||||
acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
|
||||
|
||||
tmp = ioread32(denali->reg + ACC_CLKS);
|
||||
|
@ -982,7 +983,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
iowrite32(tmp, denali->reg + ACC_CLKS);
|
||||
|
||||
/* tRWH -> RE_2_WE */
|
||||
re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
|
||||
re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_x);
|
||||
re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
|
||||
|
||||
tmp = ioread32(denali->reg + RE_2_WE);
|
||||
|
@ -991,7 +992,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
iowrite32(tmp, denali->reg + RE_2_WE);
|
||||
|
||||
/* tRHZ -> RE_2_RE */
|
||||
re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
|
||||
re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_x);
|
||||
re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
|
||||
|
||||
tmp = ioread32(denali->reg + RE_2_RE);
|
||||
|
@ -1005,8 +1006,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
* With WE_2_RE properly set, the Denali controller automatically takes
|
||||
* care of the delay; the driver need not set NAND_WAIT_TCCS.
|
||||
*/
|
||||
we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min),
|
||||
t_clk);
|
||||
we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), t_x);
|
||||
we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
|
||||
|
||||
tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE);
|
||||
|
@ -1021,7 +1021,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
if (denali->revision < 0x0501)
|
||||
addr_2_data_mask >>= 1;
|
||||
|
||||
addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
|
||||
addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_x);
|
||||
addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
|
||||
|
||||
tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);
|
||||
|
@ -1031,7 +1031,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
|
||||
/* tREH, tWH -> RDWR_EN_HI_CNT */
|
||||
rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
|
||||
t_clk);
|
||||
t_x);
|
||||
rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
|
||||
|
||||
tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);
|
||||
|
@ -1040,11 +1040,10 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);
|
||||
|
||||
/* tRP, tWP -> RDWR_EN_LO_CNT */
|
||||
rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
|
||||
t_clk);
|
||||
rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), t_x);
|
||||
rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
|
||||
t_clk);
|
||||
rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
|
||||
t_x);
|
||||
rdwr_en_lo_hi = max_t(int, rdwr_en_lo_hi, mult_x);
|
||||
rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
|
||||
rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
|
||||
|
||||
|
@ -1054,8 +1053,8 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);
|
||||
|
||||
/* tCS, tCEA -> CS_SETUP_CNT */
|
||||
cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
|
||||
(int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
|
||||
cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_x) - rdwr_en_lo,
|
||||
(int)DIV_ROUND_UP(timings->tCEA_max, t_x) - acc_clks,
|
||||
0);
|
||||
cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
|
||||
|
||||
|
@ -1120,33 +1119,6 @@ int denali_calc_ecc_bytes(int step_size, int strength)
|
|||
}
|
||||
EXPORT_SYMBOL(denali_calc_ecc_bytes);
|
||||
|
||||
static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
struct denali_nand_info *denali)
|
||||
{
|
||||
int oobavail = mtd->oobsize - denali->oob_skip_bytes;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If .size and .strength are already set (usually by DT),
|
||||
* check if they are supported by this controller.
|
||||
*/
|
||||
if (chip->ecc.size && chip->ecc.strength)
|
||||
return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail);
|
||||
|
||||
/*
|
||||
* We want .size and .strength closest to the chip's requirement
|
||||
* unless NAND_ECC_MAXIMIZE is requested.
|
||||
*/
|
||||
if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
|
||||
ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Max ECC strength is the last thing we can do */
|
||||
return nand_maximize_ecc(chip, denali->ecc_caps, oobavail);
|
||||
}
|
||||
|
||||
static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
|
@ -1233,6 +1205,115 @@ static int denali_multidev_fixup(struct denali_nand_info *denali)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int denali_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
int ret;
|
||||
|
||||
if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
|
||||
denali->dma_avail = 1;
|
||||
|
||||
if (denali->dma_avail) {
|
||||
int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
|
||||
|
||||
ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
|
||||
if (ret) {
|
||||
dev_info(denali->dev,
|
||||
"Failed to set DMA mask. Disabling DMA.\n");
|
||||
denali->dma_avail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (denali->dma_avail) {
|
||||
chip->options |= NAND_USE_BOUNCE_BUFFER;
|
||||
chip->buf_align = 16;
|
||||
if (denali->caps & DENALI_CAP_DMA_64BIT)
|
||||
denali->setup_dma = denali_setup_dma64;
|
||||
else
|
||||
denali->setup_dma = denali_setup_dma32;
|
||||
}
|
||||
|
||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
chip->ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
ret = nand_ecc_choose_conf(chip, denali->ecc_caps,
|
||||
mtd->oobsize - denali->oob_skip_bytes);
|
||||
if (ret) {
|
||||
dev_err(denali->dev, "Failed to setup ECC settings.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(denali->dev,
|
||||
"chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
|
||||
chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
|
||||
|
||||
iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) |
|
||||
FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength),
|
||||
denali->reg + ECC_CORRECTION);
|
||||
iowrite32(mtd->erasesize / mtd->writesize,
|
||||
denali->reg + PAGES_PER_BLOCK);
|
||||
iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
|
||||
denali->reg + DEVICE_WIDTH);
|
||||
iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG,
|
||||
denali->reg + TWO_ROW_ADDR_CYCLES);
|
||||
iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
|
||||
iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
|
||||
|
||||
iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
|
||||
iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
|
||||
/* chip->ecc.steps is set by nand_scan_tail(); not available here */
|
||||
iowrite32(mtd->writesize / chip->ecc.size,
|
||||
denali->reg + CFG_NUM_DATA_BLOCKS);
|
||||
|
||||
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
|
||||
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
chip->read_buf = denali_read_buf16;
|
||||
chip->write_buf = denali_write_buf16;
|
||||
} else {
|
||||
chip->read_buf = denali_read_buf;
|
||||
chip->write_buf = denali_write_buf;
|
||||
}
|
||||
chip->ecc.read_page = denali_read_page;
|
||||
chip->ecc.read_page_raw = denali_read_page_raw;
|
||||
chip->ecc.write_page = denali_write_page;
|
||||
chip->ecc.write_page_raw = denali_write_page_raw;
|
||||
chip->ecc.read_oob = denali_read_oob;
|
||||
chip->ecc.write_oob = denali_write_oob;
|
||||
chip->erase = denali_erase;
|
||||
|
||||
ret = denali_multidev_fixup(denali);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not
|
||||
* use devm_kmalloc() because the memory allocated by devm_ does not
|
||||
* guarantee DMA-safe alignment.
|
||||
*/
|
||||
denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
|
||||
if (!denali->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void denali_detach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
|
||||
kfree(denali->buf);
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops denali_controller_ops = {
|
||||
.attach_chip = denali_attach_chip,
|
||||
.detach_chip = denali_detach_chip,
|
||||
};
|
||||
|
||||
int denali_init(struct denali_nand_info *denali)
|
||||
{
|
||||
struct nand_chip *chip = &denali->nand;
|
||||
|
@ -1282,116 +1363,24 @@ int denali_init(struct denali_nand_info *denali)
|
|||
}
|
||||
|
||||
/* clk rate info is needed for setup_data_interface */
|
||||
if (denali->clk_x_rate)
|
||||
if (denali->clk_rate && denali->clk_x_rate)
|
||||
chip->setup_data_interface = denali_setup_data_interface;
|
||||
|
||||
ret = nand_scan_ident(mtd, denali->max_banks, NULL);
|
||||
chip->dummy_controller.ops = &denali_controller_ops;
|
||||
ret = nand_scan(mtd, denali->max_banks);
|
||||
if (ret)
|
||||
goto disable_irq;
|
||||
|
||||
if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
|
||||
denali->dma_avail = 1;
|
||||
|
||||
if (denali->dma_avail) {
|
||||
int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
|
||||
|
||||
ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
|
||||
if (ret) {
|
||||
dev_info(denali->dev,
|
||||
"Failed to set DMA mask. Disabling DMA.\n");
|
||||
denali->dma_avail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (denali->dma_avail) {
|
||||
chip->options |= NAND_USE_BOUNCE_BUFFER;
|
||||
chip->buf_align = 16;
|
||||
if (denali->caps & DENALI_CAP_DMA_64BIT)
|
||||
denali->setup_dma = denali_setup_dma64;
|
||||
else
|
||||
denali->setup_dma = denali_setup_dma32;
|
||||
}
|
||||
|
||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
chip->ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
ret = denali_ecc_setup(mtd, chip, denali);
|
||||
if (ret) {
|
||||
dev_err(denali->dev, "Failed to setup ECC settings.\n");
|
||||
goto disable_irq;
|
||||
}
|
||||
|
||||
dev_dbg(denali->dev,
|
||||
"chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
|
||||
chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
|
||||
|
||||
iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) |
|
||||
FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength),
|
||||
denali->reg + ECC_CORRECTION);
|
||||
iowrite32(mtd->erasesize / mtd->writesize,
|
||||
denali->reg + PAGES_PER_BLOCK);
|
||||
iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
|
||||
denali->reg + DEVICE_WIDTH);
|
||||
iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG,
|
||||
denali->reg + TWO_ROW_ADDR_CYCLES);
|
||||
iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
|
||||
iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
|
||||
|
||||
iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
|
||||
iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
|
||||
/* chip->ecc.steps is set by nand_scan_tail(); not available here */
|
||||
iowrite32(mtd->writesize / chip->ecc.size,
|
||||
denali->reg + CFG_NUM_DATA_BLOCKS);
|
||||
|
||||
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
|
||||
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
chip->read_buf = denali_read_buf16;
|
||||
chip->write_buf = denali_write_buf16;
|
||||
} else {
|
||||
chip->read_buf = denali_read_buf;
|
||||
chip->write_buf = denali_write_buf;
|
||||
}
|
||||
chip->ecc.read_page = denali_read_page;
|
||||
chip->ecc.read_page_raw = denali_read_page_raw;
|
||||
chip->ecc.write_page = denali_write_page;
|
||||
chip->ecc.write_page_raw = denali_write_page_raw;
|
||||
chip->ecc.read_oob = denali_read_oob;
|
||||
chip->ecc.write_oob = denali_write_oob;
|
||||
chip->erase = denali_erase;
|
||||
|
||||
ret = denali_multidev_fixup(denali);
|
||||
if (ret)
|
||||
goto disable_irq;
|
||||
|
||||
/*
|
||||
* This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not
|
||||
* use devm_kmalloc() because the memory allocated by devm_ does not
|
||||
* guarantee DMA-safe alignment.
|
||||
*/
|
||||
denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
|
||||
if (!denali->buf) {
|
||||
ret = -ENOMEM;
|
||||
goto disable_irq;
|
||||
}
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret)
|
||||
goto free_buf;
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
|
||||
goto cleanup_nand;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_nand:
|
||||
nand_cleanup(chip);
|
||||
free_buf:
|
||||
kfree(denali->buf);
|
||||
disable_irq:
|
||||
denali_disable_irq(denali);
|
||||
|
||||
|
@ -1404,7 +1393,6 @@ void denali_remove(struct denali_nand_info *denali)
|
|||
struct mtd_info *mtd = nand_to_mtd(&denali->nand);
|
||||
|
||||
nand_release(mtd);
|
||||
kfree(denali->buf);
|
||||
denali_disable_irq(denali);
|
||||
}
|
||||
EXPORT_SYMBOL(denali_remove);
|
||||
|
|
|
@ -300,6 +300,7 @@
|
|||
|
||||
struct denali_nand_info {
|
||||
struct nand_chip nand;
|
||||
unsigned long clk_rate; /* core clock rate */
|
||||
unsigned long clk_x_rate; /* bus interface clock rate */
|
||||
int active_bank; /* currently selected bank */
|
||||
struct device *dev;
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
|
||||
struct denali_dt {
|
||||
struct denali_nand_info denali;
|
||||
struct clk *clk;
|
||||
struct clk *clk; /* core clock */
|
||||
struct clk *clk_x; /* bus interface clock */
|
||||
struct clk *clk_ecc; /* ECC circuit clock */
|
||||
};
|
||||
|
||||
struct denali_dt_data {
|
||||
|
@ -79,63 +81,99 @@ MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
|
|||
|
||||
static int denali_dt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct denali_dt *dt;
|
||||
const struct denali_dt_data *data;
|
||||
struct denali_nand_info *denali;
|
||||
int ret;
|
||||
|
||||
dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL);
|
||||
dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
|
||||
if (!dt)
|
||||
return -ENOMEM;
|
||||
denali = &dt->denali;
|
||||
|
||||
data = of_device_get_match_data(&pdev->dev);
|
||||
data = of_device_get_match_data(dev);
|
||||
if (data) {
|
||||
denali->revision = data->revision;
|
||||
denali->caps = data->caps;
|
||||
denali->ecc_caps = data->ecc_caps;
|
||||
}
|
||||
|
||||
denali->dev = &pdev->dev;
|
||||
denali->dev = dev;
|
||||
denali->irq = platform_get_irq(pdev, 0);
|
||||
if (denali->irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq defined\n");
|
||||
dev_err(dev, "no irq defined\n");
|
||||
return denali->irq;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg");
|
||||
denali->reg = devm_ioremap_resource(&pdev->dev, res);
|
||||
denali->reg = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(denali->reg))
|
||||
return PTR_ERR(denali->reg);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
|
||||
denali->host = devm_ioremap_resource(&pdev->dev, res);
|
||||
denali->host = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(denali->host))
|
||||
return PTR_ERR(denali->host);
|
||||
|
||||
dt->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
/*
|
||||
* A single anonymous clock is supported for the backward compatibility.
|
||||
* New platforms should support all the named clocks.
|
||||
*/
|
||||
dt->clk = devm_clk_get(dev, "nand");
|
||||
if (IS_ERR(dt->clk))
|
||||
dt->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(dt->clk)) {
|
||||
dev_err(&pdev->dev, "no clk available\n");
|
||||
dev_err(dev, "no clk available\n");
|
||||
return PTR_ERR(dt->clk);
|
||||
}
|
||||
|
||||
dt->clk_x = devm_clk_get(dev, "nand_x");
|
||||
if (IS_ERR(dt->clk_x))
|
||||
dt->clk_x = NULL;
|
||||
|
||||
dt->clk_ecc = devm_clk_get(dev, "ecc");
|
||||
if (IS_ERR(dt->clk_ecc))
|
||||
dt->clk_ecc = NULL;
|
||||
|
||||
ret = clk_prepare_enable(dt->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Hardcode the clock rate for the backward compatibility.
|
||||
* This works for both SOCFPGA and UniPhier.
|
||||
*/
|
||||
denali->clk_x_rate = 200000000;
|
||||
ret = clk_prepare_enable(dt->clk_x);
|
||||
if (ret)
|
||||
goto out_disable_clk;
|
||||
|
||||
ret = clk_prepare_enable(dt->clk_ecc);
|
||||
if (ret)
|
||||
goto out_disable_clk_x;
|
||||
|
||||
if (dt->clk_x) {
|
||||
denali->clk_rate = clk_get_rate(dt->clk);
|
||||
denali->clk_x_rate = clk_get_rate(dt->clk_x);
|
||||
} else {
|
||||
/*
|
||||
* Hardcode the clock rates for the backward compatibility.
|
||||
* This works for both SOCFPGA and UniPhier.
|
||||
*/
|
||||
dev_notice(dev,
|
||||
"necessary clock is missing. default clock rates are used.\n");
|
||||
denali->clk_rate = 50000000;
|
||||
denali->clk_x_rate = 200000000;
|
||||
}
|
||||
|
||||
ret = denali_init(denali);
|
||||
if (ret)
|
||||
goto out_disable_clk;
|
||||
goto out_disable_clk_ecc;
|
||||
|
||||
platform_set_drvdata(pdev, dt);
|
||||
return 0;
|
||||
|
||||
out_disable_clk_ecc:
|
||||
clk_disable_unprepare(dt->clk_ecc);
|
||||
out_disable_clk_x:
|
||||
clk_disable_unprepare(dt->clk_x);
|
||||
out_disable_clk:
|
||||
clk_disable_unprepare(dt->clk);
|
||||
|
||||
|
@ -147,6 +185,8 @@ static int denali_dt_remove(struct platform_device *pdev)
|
|||
struct denali_dt *dt = platform_get_drvdata(pdev);
|
||||
|
||||
denali_remove(&dt->denali);
|
||||
clk_disable_unprepare(dt->clk_ecc);
|
||||
clk_disable_unprepare(dt->clk_x);
|
||||
clk_disable_unprepare(dt->clk);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -73,6 +73,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
denali->irq = dev->irq;
|
||||
denali->ecc_caps = &denali_pci_ecc_caps;
|
||||
denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
|
||||
denali->clk_rate = 50000000; /* 50 MHz */
|
||||
denali->clk_x_rate = 200000000; /* 200 MHz */
|
||||
|
||||
ret = pci_request_regions(dev, DENALI_NAND_NAME);
|
||||
|
|
|
@ -1291,7 +1291,7 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd)
|
|||
this->bbt_md = NULL;
|
||||
}
|
||||
|
||||
ret = this->scan_bbt(mtd);
|
||||
ret = nand_create_bbt(this);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1338,7 +1338,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
|
|||
this->bbt_md->pattern = "TBB_SYSM";
|
||||
}
|
||||
|
||||
ret = this->scan_bbt(mtd);
|
||||
ret = nand_create_bbt(this);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -1227,10 +1227,9 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
|
|||
* required within a nand driver because they are performed by the nand
|
||||
* infrastructure code as part of nand_scan(). In this case they need
|
||||
* to be initialized here because we skip call to nand_scan_ident() (the
|
||||
* first half of nand_scan()). The call to nand_scan_ident() is skipped
|
||||
* because for this device the chip id is not read in the manner of a
|
||||
* standard nand device. Unfortunately, nand_scan_ident() does other
|
||||
* things as well, such as call nand_set_defaults().
|
||||
* first half of nand_scan()). The call to nand_scan_ident() could be
|
||||
* skipped because for this device the chip id is not read in the manner
|
||||
* of a standard nand device.
|
||||
*/
|
||||
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
|
@ -1257,8 +1256,8 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
|
|||
nand->ecc.strength = DOCG4_T;
|
||||
nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE;
|
||||
nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA;
|
||||
nand->controller = &nand->hwcontrol;
|
||||
nand_hw_control_init(nand->controller);
|
||||
nand->controller = &nand->dummy_controller;
|
||||
nand_controller_init(nand->controller);
|
||||
|
||||
/* methods */
|
||||
nand->cmdfunc = docg4_command;
|
||||
|
@ -1315,6 +1314,40 @@ static int __init read_id_reg(struct mtd_info *mtd)
|
|||
|
||||
static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL };
|
||||
|
||||
static int docg4_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct docg4_priv *doc = (struct docg4_priv *)(chip + 1);
|
||||
int ret;
|
||||
|
||||
init_mtd_structs(mtd);
|
||||
|
||||
/* Initialize kernel BCH algorithm */
|
||||
doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY);
|
||||
if (!doc->bch)
|
||||
return -EINVAL;
|
||||
|
||||
reset(mtd);
|
||||
|
||||
ret = read_id_reg(mtd);
|
||||
if (ret)
|
||||
free_bch(doc->bch);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void docg4_detach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct docg4_priv *doc = (struct docg4_priv *)(chip + 1);
|
||||
|
||||
free_bch(doc->bch);
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops docg4_controller_ops = {
|
||||
.attach_chip = docg4_attach_chip,
|
||||
.detach_chip = docg4_detach_chip,
|
||||
};
|
||||
|
||||
static int __init probe_docg4(struct platform_device *pdev)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
|
@ -1341,7 +1374,7 @@ static int __init probe_docg4(struct platform_device *pdev)
|
|||
nand = kzalloc(len, GFP_KERNEL);
|
||||
if (nand == NULL) {
|
||||
retval = -ENOMEM;
|
||||
goto fail_unmap;
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
mtd = nand_to_mtd(nand);
|
||||
|
@ -1350,46 +1383,35 @@ static int __init probe_docg4(struct platform_device *pdev)
|
|||
mtd->dev.parent = &pdev->dev;
|
||||
doc->virtadr = virtadr;
|
||||
doc->dev = dev;
|
||||
|
||||
init_mtd_structs(mtd);
|
||||
|
||||
/* initialize kernel bch algorithm */
|
||||
doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY);
|
||||
if (doc->bch == NULL) {
|
||||
retval = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, doc);
|
||||
|
||||
reset(mtd);
|
||||
retval = read_id_reg(mtd);
|
||||
if (retval == -ENODEV) {
|
||||
dev_warn(dev, "No diskonchip G4 device found.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
retval = nand_scan_tail(mtd);
|
||||
/*
|
||||
* Running nand_scan() with maxchips == 0 will skip nand_scan_ident(),
|
||||
* which is a specific operation with this driver and done in the
|
||||
* ->attach_chip callback.
|
||||
*/
|
||||
nand->dummy_controller.ops = &docg4_controller_ops;
|
||||
retval = nand_scan(mtd, 0);
|
||||
if (retval)
|
||||
goto fail;
|
||||
goto free_nand;
|
||||
|
||||
retval = read_factory_bbt(mtd);
|
||||
if (retval)
|
||||
goto fail;
|
||||
goto cleanup_nand;
|
||||
|
||||
retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
|
||||
if (retval)
|
||||
goto fail;
|
||||
goto cleanup_nand;
|
||||
|
||||
doc->mtd = mtd;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
nand_release(mtd); /* deletes partitions and mtd devices */
|
||||
free_bch(doc->bch);
|
||||
cleanup_nand:
|
||||
nand_cleanup(nand);
|
||||
free_nand:
|
||||
kfree(nand);
|
||||
|
||||
fail_unmap:
|
||||
unmap:
|
||||
iounmap(virtadr);
|
||||
|
||||
return retval;
|
||||
|
@ -1399,7 +1421,6 @@ static int __exit cleanup_docg4(struct platform_device *pdev)
|
|||
{
|
||||
struct docg4_priv *doc = platform_get_drvdata(pdev);
|
||||
nand_release(doc->mtd);
|
||||
free_bch(doc->bch);
|
||||
kfree(mtd_to_nand(doc->mtd));
|
||||
iounmap(doc->virtadr);
|
||||
return 0;
|
||||
|
|
|
@ -61,7 +61,7 @@ struct fsl_elbc_mtd {
|
|||
/* Freescale eLBC FCM controller information */
|
||||
|
||||
struct fsl_elbc_fcm_ctrl {
|
||||
struct nand_hw_control controller;
|
||||
struct nand_controller controller;
|
||||
struct fsl_elbc_mtd *chips[MAX_BANKS];
|
||||
|
||||
u8 __iomem *addr; /* Address of assigned FCM buffer */
|
||||
|
@ -637,9 +637,9 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
|||
return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
|
||||
}
|
||||
|
||||
static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
|
||||
static int fsl_elbc_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
|
@ -700,12 +700,16 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
|
|||
dev_err(priv->dev,
|
||||
"fsl_elbc_init: page size %d is not supported\n",
|
||||
mtd->writesize);
|
||||
return -1;
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops fsl_elbc_controller_ops = {
|
||||
.attach_chip = fsl_elbc_attach_chip,
|
||||
};
|
||||
|
||||
static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
|
@ -879,7 +883,7 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
|
|||
}
|
||||
elbc_fcm_ctrl->counter++;
|
||||
|
||||
nand_hw_control_init(&elbc_fcm_ctrl->controller);
|
||||
nand_controller_init(&elbc_fcm_ctrl->controller);
|
||||
fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
|
||||
} else {
|
||||
elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
|
||||
|
@ -910,15 +914,8 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nand_scan_ident(mtd, 1, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = fsl_elbc_chip_init_tail(mtd);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
priv->chip.controller->ops = &fsl_elbc_controller_ops;
|
||||
ret = nand_scan(mtd, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ struct fsl_ifc_mtd {
|
|||
|
||||
/* overview of the fsl ifc controller */
|
||||
struct fsl_ifc_nand_ctrl {
|
||||
struct nand_hw_control controller;
|
||||
struct nand_controller controller;
|
||||
struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT];
|
||||
|
||||
void __iomem *addr; /* Address of assigned IFC buffer */
|
||||
|
@ -225,7 +225,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
|
|||
int bufnum = nctrl->page & priv->bufnum_mask;
|
||||
int sector_start = bufnum * chip->ecc.steps;
|
||||
int sector_end = sector_start + chip->ecc.steps - 1;
|
||||
__be32 *eccstat_regs;
|
||||
__be32 __iomem *eccstat_regs;
|
||||
|
||||
eccstat_regs = ifc->ifc_nand.nand_eccstat;
|
||||
eccstat = ifc_in32(&eccstat_regs[sector_start / 4]);
|
||||
|
@ -714,9 +714,9 @@ static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
return nand_prog_page_end_op(chip);
|
||||
}
|
||||
|
||||
static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
|
||||
static int fsl_ifc_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
|
||||
|
||||
dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__,
|
||||
|
@ -757,6 +757,10 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops fsl_ifc_controller_ops = {
|
||||
.attach_chip = fsl_ifc_attach_chip,
|
||||
};
|
||||
|
||||
static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
|
||||
{
|
||||
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
|
||||
|
@ -1004,7 +1008,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
|
|||
ifc_nand_ctrl->addr = NULL;
|
||||
fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl;
|
||||
|
||||
nand_hw_control_init(&ifc_nand_ctrl->controller);
|
||||
nand_controller_init(&ifc_nand_ctrl->controller);
|
||||
} else {
|
||||
ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand;
|
||||
}
|
||||
|
@ -1046,15 +1050,8 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nand_scan_ident(mtd, 1, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = fsl_ifc_chip_init_tail(mtd);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
priv->chip.controller->ops = &fsl_ifc_controller_ops;
|
||||
ret = nand_scan(mtd, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
reg)
|
||||
|
||||
/* fsmc controller registers for NAND flash */
|
||||
#define PC 0x00
|
||||
#define FSMC_PC 0x00
|
||||
/* pc register definitions */
|
||||
#define FSMC_RESET (1 << 0)
|
||||
#define FSMC_WAITON (1 << 1)
|
||||
|
@ -273,12 +273,13 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host,
|
|||
tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
|
||||
|
||||
if (host->nand.options & NAND_BUSWIDTH_16)
|
||||
writel_relaxed(value | FSMC_DEVWID_16, host->regs_va + PC);
|
||||
writel_relaxed(value | FSMC_DEVWID_16,
|
||||
host->regs_va + FSMC_PC);
|
||||
else
|
||||
writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + PC);
|
||||
writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + FSMC_PC);
|
||||
|
||||
writel_relaxed(readl(host->regs_va + PC) | tclr | tar,
|
||||
host->regs_va + PC);
|
||||
writel_relaxed(readl(host->regs_va + FSMC_PC) | tclr | tar,
|
||||
host->regs_va + FSMC_PC);
|
||||
writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM);
|
||||
writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB);
|
||||
}
|
||||
|
@ -371,12 +372,12 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
|
|||
{
|
||||
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||
|
||||
writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCPLEN_256,
|
||||
host->regs_va + PC);
|
||||
writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCEN,
|
||||
host->regs_va + PC);
|
||||
writel_relaxed(readl(host->regs_va + PC) | FSMC_ECCEN,
|
||||
host->regs_va + PC);
|
||||
writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCPLEN_256,
|
||||
host->regs_va + FSMC_PC);
|
||||
writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCEN,
|
||||
host->regs_va + FSMC_PC);
|
||||
writel_relaxed(readl(host->regs_va + FSMC_PC) | FSMC_ECCEN,
|
||||
host->regs_va + FSMC_PC);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -546,7 +547,7 @@ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
|||
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||
int i;
|
||||
|
||||
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
|
||||
if (IS_ALIGNED((uintptr_t)buf, sizeof(uint32_t)) &&
|
||||
IS_ALIGNED(len, sizeof(uint32_t))) {
|
||||
uint32_t *p = (uint32_t *)buf;
|
||||
len = len >> 2;
|
||||
|
@ -569,7 +570,7 @@ static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|||
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||
int i;
|
||||
|
||||
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
|
||||
if (IS_ALIGNED((uintptr_t)buf, sizeof(uint32_t)) &&
|
||||
IS_ALIGNED(len, sizeof(uint32_t))) {
|
||||
uint32_t *p = (uint32_t *)buf;
|
||||
len = len >> 2;
|
||||
|
@ -618,11 +619,11 @@ static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
|
|||
if (chipnr > 0)
|
||||
return;
|
||||
|
||||
pc = readl(host->regs_va + PC);
|
||||
pc = readl(host->regs_va + FSMC_PC);
|
||||
if (chipnr < 0)
|
||||
writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + PC);
|
||||
writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + FSMC_PC);
|
||||
else
|
||||
writel_relaxed(pc | FSMC_ENABLE, host->regs_va + PC);
|
||||
writel_relaxed(pc | FSMC_ENABLE, host->regs_va + FSMC_PC);
|
||||
|
||||
/* nCE line must be asserted before starting any operation */
|
||||
mb();
|
||||
|
@ -740,7 +741,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
|
||||
nand_read_page_op(chip, page, s * eccsize, NULL, 0);
|
||||
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
chip->read_buf(mtd, p, eccsize);
|
||||
nand_read_data_op(chip, p, eccsize, false);
|
||||
|
||||
for (j = 0; j < eccbytes;) {
|
||||
struct mtd_oob_region oobregion;
|
||||
|
@ -918,6 +919,82 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsmc_nand_attach_chip(struct nand_chip *nand)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||
|
||||
if (AMBA_REV_BITS(host->pid) >= 8) {
|
||||
switch (mtd->oobsize) {
|
||||
case 16:
|
||||
case 64:
|
||||
case 128:
|
||||
case 224:
|
||||
case 256:
|
||||
break;
|
||||
default:
|
||||
dev_warn(host->dev,
|
||||
"No oob scheme defined for oobsize %d\n",
|
||||
mtd->oobsize);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (nand->ecc.mode) {
|
||||
case NAND_ECC_HW:
|
||||
dev_info(host->dev, "Using 1-bit HW ECC scheme\n");
|
||||
nand->ecc.calculate = fsmc_read_hwecc_ecc1;
|
||||
nand->ecc.correct = nand_correct_data;
|
||||
nand->ecc.bytes = 3;
|
||||
nand->ecc.strength = 1;
|
||||
break;
|
||||
|
||||
case NAND_ECC_SOFT:
|
||||
if (nand->ecc.algo == NAND_ECC_BCH) {
|
||||
dev_info(host->dev,
|
||||
"Using 4-bit SW BCH ECC scheme\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case NAND_ECC_ON_DIE:
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(host->dev, "Unsupported ECC mode!\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't set layout for BCH4 SW ECC. This will be
|
||||
* generated later in nand_bch_init() later.
|
||||
*/
|
||||
if (nand->ecc.mode == NAND_ECC_HW) {
|
||||
switch (mtd->oobsize) {
|
||||
case 16:
|
||||
case 64:
|
||||
case 128:
|
||||
mtd_set_ooblayout(mtd,
|
||||
&fsmc_ecc1_ooblayout_ops);
|
||||
break;
|
||||
default:
|
||||
dev_warn(host->dev,
|
||||
"No oob scheme defined for oobsize %d\n",
|
||||
mtd->oobsize);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops fsmc_nand_controller_ops = {
|
||||
.attach_chip = fsmc_nand_attach_chip,
|
||||
};
|
||||
|
||||
/*
|
||||
* fsmc_nand_probe - Probe function
|
||||
* @pdev: platform device structure
|
||||
|
@ -1047,76 +1124,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
|||
/*
|
||||
* Scan to find existence of the device
|
||||
*/
|
||||
ret = nand_scan_ident(mtd, 1, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "No NAND Device found!\n");
|
||||
goto release_dma_write_chan;
|
||||
}
|
||||
|
||||
if (AMBA_REV_BITS(host->pid) >= 8) {
|
||||
switch (mtd->oobsize) {
|
||||
case 16:
|
||||
case 64:
|
||||
case 128:
|
||||
case 224:
|
||||
case 256:
|
||||
break;
|
||||
default:
|
||||
dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n",
|
||||
mtd->oobsize);
|
||||
ret = -EINVAL;
|
||||
goto release_dma_write_chan;
|
||||
}
|
||||
|
||||
mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops);
|
||||
} else {
|
||||
switch (nand->ecc.mode) {
|
||||
case NAND_ECC_HW:
|
||||
dev_info(&pdev->dev, "Using 1-bit HW ECC scheme\n");
|
||||
nand->ecc.calculate = fsmc_read_hwecc_ecc1;
|
||||
nand->ecc.correct = nand_correct_data;
|
||||
nand->ecc.bytes = 3;
|
||||
nand->ecc.strength = 1;
|
||||
break;
|
||||
|
||||
case NAND_ECC_SOFT:
|
||||
if (nand->ecc.algo == NAND_ECC_BCH) {
|
||||
dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case NAND_ECC_ON_DIE:
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&pdev->dev, "Unsupported ECC mode!\n");
|
||||
goto release_dma_write_chan;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't set layout for BCH4 SW ECC. This will be
|
||||
* generated later in nand_bch_init() later.
|
||||
*/
|
||||
if (nand->ecc.mode == NAND_ECC_HW) {
|
||||
switch (mtd->oobsize) {
|
||||
case 16:
|
||||
case 64:
|
||||
case 128:
|
||||
mtd_set_ooblayout(mtd,
|
||||
&fsmc_ecc1_ooblayout_ops);
|
||||
break;
|
||||
default:
|
||||
dev_warn(&pdev->dev,
|
||||
"No oob scheme defined for oobsize %d\n",
|
||||
mtd->oobsize);
|
||||
ret = -EINVAL;
|
||||
goto release_dma_write_chan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Second stage of scan to fill MTD data-structures */
|
||||
ret = nand_scan_tail(mtd);
|
||||
nand->dummy_controller.ops = &fsmc_nand_controller_ops;
|
||||
ret = nand_scan(mtd, 1);
|
||||
if (ret)
|
||||
goto release_dma_write_chan;
|
||||
|
||||
|
|
|
@ -1,22 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Freescale GPMI NAND Flash Driver
|
||||
*
|
||||
* Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
|
|
|
@ -1,22 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Freescale GPMI NAND Flash Driver
|
||||
*
|
||||
* Copyright (C) 2010-2015 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -757,9 +744,9 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
|
|||
* [2] Allocate a read/write data buffer.
|
||||
* The gpmi_alloc_dma_buffer can be called twice.
|
||||
* We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer
|
||||
* is called before the nand_scan_ident; and we allocate a buffer
|
||||
* of the real NAND page size when the gpmi_alloc_dma_buffer is
|
||||
* called after the nand_scan_ident.
|
||||
* is called before the NAND identification; and we allocate a
|
||||
* buffer of the real NAND page size when the gpmi_alloc_dma_buffer
|
||||
* is called after.
|
||||
*/
|
||||
this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE,
|
||||
GFP_DMA | GFP_KERNEL);
|
||||
|
@ -957,7 +944,6 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip,
|
|||
struct gpmi_nand_data *this = nand_get_controller_data(chip);
|
||||
struct bch_geometry *nfc_geo = &this->bch_geometry;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
void *payload_virt;
|
||||
dma_addr_t payload_phys;
|
||||
unsigned int i;
|
||||
unsigned char *status;
|
||||
|
@ -967,7 +953,6 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip,
|
|||
|
||||
dev_dbg(this->dev, "page number is : %d\n", page);
|
||||
|
||||
payload_virt = this->payload_virt;
|
||||
payload_phys = this->payload_phys;
|
||||
|
||||
if (virt_addr_valid(buf)) {
|
||||
|
@ -976,7 +961,6 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip,
|
|||
dest_phys = dma_map_single(this->dev, buf, nfc_geo->payload_size,
|
||||
DMA_FROM_DEVICE);
|
||||
if (!dma_mapping_error(this->dev, dest_phys)) {
|
||||
payload_virt = buf;
|
||||
payload_phys = dest_phys;
|
||||
direct = true;
|
||||
}
|
||||
|
@ -1881,6 +1865,34 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gpmi_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct gpmi_nand_data *this = nand_get_controller_data(chip);
|
||||
int ret;
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
if (of_property_read_bool(this->dev->of_node,
|
||||
"fsl,no-blockmark-swap"))
|
||||
this->swap_block_mark = false;
|
||||
}
|
||||
dev_dbg(this->dev, "Blockmark swapping %sabled\n",
|
||||
this->swap_block_mark ? "en" : "dis");
|
||||
|
||||
ret = gpmi_init_last(this);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops gpmi_nand_controller_ops = {
|
||||
.attach_chip = gpmi_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int gpmi_nand_init(struct gpmi_nand_data *this)
|
||||
{
|
||||
struct nand_chip *chip = &this->nand;
|
||||
|
@ -1921,33 +1933,15 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
|
|||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
if (of_property_read_bool(this->dev->of_node,
|
||||
"fsl,no-blockmark-swap"))
|
||||
this->swap_block_mark = false;
|
||||
}
|
||||
dev_dbg(this->dev, "Blockmark swapping %sabled\n",
|
||||
this->swap_block_mark ? "en" : "dis");
|
||||
|
||||
ret = gpmi_init_last(this);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
chip->options |= NAND_SKIP_BBTSCAN;
|
||||
ret = nand_scan_tail(mtd);
|
||||
chip->dummy_controller.ops = &gpmi_nand_controller_ops;
|
||||
ret = nand_scan(mtd, GPMI_IS_MX6(this) ? 2 : 1);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
ret = nand_boot_init(this);
|
||||
if (ret)
|
||||
goto err_nand_cleanup;
|
||||
ret = chip->scan_bbt(mtd);
|
||||
ret = nand_create_bbt(chip);
|
||||
if (ret)
|
||||
goto err_nand_cleanup;
|
||||
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Freescale GPMI NAND Flash Driver
|
||||
*
|
||||
* Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H
|
||||
#define __DRIVERS_MTD_NAND_GPMI_NAND_H
|
||||
|
|
|
@ -709,9 +709,50 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hisi_nfc_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct hinfc_host *host = nand_get_controller_data(chip);
|
||||
int flag;
|
||||
|
||||
host->buffer = dmam_alloc_coherent(host->dev,
|
||||
mtd->writesize + mtd->oobsize,
|
||||
&host->dma_buffer, GFP_KERNEL);
|
||||
if (!host->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
host->dma_oob = host->dma_buffer + mtd->writesize;
|
||||
memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
|
||||
|
||||
flag = hinfc_read(host, HINFC504_CON);
|
||||
flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT);
|
||||
switch (mtd->writesize) {
|
||||
case 2048:
|
||||
flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT);
|
||||
break;
|
||||
/*
|
||||
* TODO: add more pagesize support,
|
||||
* default pagesize has been set in hisi_nfc_host_init
|
||||
*/
|
||||
default:
|
||||
dev_err(host->dev, "NON-2KB page size nand flash\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
hinfc_write(host, flag, HINFC504_CON);
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_HW)
|
||||
hisi_nfc_ecc_probe(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops hisi_nfc_controller_ops = {
|
||||
.attach_chip = hisi_nfc_attach_chip,
|
||||
};
|
||||
|
||||
static int hisi_nfc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP;
|
||||
int ret = 0, irq, max_chips = HINFC504_MAX_CHIP;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct hinfc_host *host;
|
||||
struct nand_chip *chip;
|
||||
|
@ -769,42 +810,11 @@ static int hisi_nfc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = nand_scan_ident(mtd, max_chips, NULL);
|
||||
chip->dummy_controller.ops = &hisi_nfc_controller_ops;
|
||||
ret = nand_scan(mtd, max_chips);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize,
|
||||
&host->dma_buffer, GFP_KERNEL);
|
||||
if (!host->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
host->dma_oob = host->dma_buffer + mtd->writesize;
|
||||
memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
|
||||
|
||||
flag = hinfc_read(host, HINFC504_CON);
|
||||
flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT);
|
||||
switch (mtd->writesize) {
|
||||
case 2048:
|
||||
flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break;
|
||||
/*
|
||||
* TODO: add more pagesize support,
|
||||
* default pagesize has been set in hisi_nfc_host_init
|
||||
*/
|
||||
default:
|
||||
dev_err(dev, "NON-2KB page size nand flash\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
hinfc_write(host, flag, HINFC504_CON);
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_HW)
|
||||
hisi_nfc_ecc_probe(host);
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret) {
|
||||
dev_err(dev, "nand_scan_tail failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "Err MTD partition=%d\n", ret);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -23,9 +24,9 @@
|
|||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <asm/mach-jz4740/jz4740_nand.h>
|
||||
#include <linux/platform_data/jz4740/jz4740_nand.h>
|
||||
|
||||
#define JZ_REG_NAND_CTRL 0x50
|
||||
#define JZ_REG_NAND_ECC_CTRL 0x100
|
||||
|
@ -330,7 +331,7 @@ static int jz_nand_detect_bank(struct platform_device *pdev,
|
|||
|
||||
if (chipnr == 0) {
|
||||
/* Detect first chip. */
|
||||
ret = nand_scan_ident(mtd, 1, NULL);
|
||||
ret = nand_scan(mtd, 1);
|
||||
if (ret)
|
||||
goto notfound_id;
|
||||
|
||||
|
@ -355,7 +356,7 @@ static int jz_nand_detect_bank(struct platform_device *pdev,
|
|||
mtd->size += chip->chipsize;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank);
|
||||
dev_info(&pdev->dev, "Found chip %zu on bank %i\n", chipnr, bank);
|
||||
return 0;
|
||||
|
||||
notfound_id:
|
||||
|
@ -367,6 +368,24 @@ notfound_id:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int jz_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct device *dev = mtd->dev.parent;
|
||||
struct jz_nand_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
if (pdata && pdata->ident_callback)
|
||||
pdata->ident_callback(pdev, mtd, &pdata->partitions,
|
||||
&pdata->num_partitions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops jz_nand_controller_ops = {
|
||||
.attach_chip = jz_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int jz_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -410,6 +429,7 @@ static int jz_nand_probe(struct platform_device *pdev)
|
|||
chip->chip_delay = 50;
|
||||
chip->cmd_ctrl = jz_nand_cmd_ctrl;
|
||||
chip->select_chip = jz_nand_select_chip;
|
||||
chip->dummy_controller.ops = &jz_nand_controller_ops;
|
||||
|
||||
if (nand->busy_gpio)
|
||||
chip->dev_ready = jz_nand_dev_ready;
|
||||
|
@ -455,33 +475,20 @@ static int jz_nand_probe(struct platform_device *pdev)
|
|||
goto err_iounmap_mmio;
|
||||
}
|
||||
|
||||
if (pdata && pdata->ident_callback) {
|
||||
pdata->ident_callback(pdev, mtd, &pdata->partitions,
|
||||
&pdata->num_partitions);
|
||||
}
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to scan NAND\n");
|
||||
goto err_unclaim_banks;
|
||||
}
|
||||
|
||||
ret = mtd_device_parse_register(mtd, NULL, NULL,
|
||||
pdata ? pdata->partitions : NULL,
|
||||
pdata ? pdata->num_partitions : 0);
|
||||
ret = mtd_device_register(mtd, pdata ? pdata->partitions : NULL,
|
||||
pdata ? pdata->num_partitions : 0);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to add mtd device\n");
|
||||
goto err_nand_release;
|
||||
goto err_cleanup_nand;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_nand_release:
|
||||
nand_release(mtd);
|
||||
err_unclaim_banks:
|
||||
err_cleanup_nand:
|
||||
nand_cleanup(chip);
|
||||
while (chipnr--) {
|
||||
unsigned char bank = nand->banks[chipnr];
|
||||
jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
|
||||
|
|
|
@ -44,7 +44,7 @@ struct jz4780_nand_cs {
|
|||
struct jz4780_nand_controller {
|
||||
struct device *dev;
|
||||
struct jz4780_bch *bch;
|
||||
struct nand_hw_control controller;
|
||||
struct nand_controller controller;
|
||||
unsigned int num_banks;
|
||||
struct list_head chips;
|
||||
int selected;
|
||||
|
@ -65,7 +65,8 @@ static inline struct jz4780_nand_chip *to_jz4780_nand_chip(struct mtd_info *mtd)
|
|||
return container_of(mtd_to_nand(mtd), struct jz4780_nand_chip, chip);
|
||||
}
|
||||
|
||||
static inline struct jz4780_nand_controller *to_jz4780_nand_controller(struct nand_hw_control *ctrl)
|
||||
static inline struct jz4780_nand_controller
|
||||
*to_jz4780_nand_controller(struct nand_controller *ctrl)
|
||||
{
|
||||
return container_of(ctrl, struct jz4780_nand_controller, controller);
|
||||
}
|
||||
|
@ -157,9 +158,8 @@ static int jz4780_nand_ecc_correct(struct mtd_info *mtd, u8 *dat,
|
|||
return jz4780_bch_correct(nfc->bch, ¶ms, dat, read_ecc);
|
||||
}
|
||||
|
||||
static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *dev)
|
||||
static int jz4780_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = &nand->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller);
|
||||
int eccbytes;
|
||||
|
@ -170,7 +170,8 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
|
|||
switch (chip->ecc.mode) {
|
||||
case NAND_ECC_HW:
|
||||
if (!nfc->bch) {
|
||||
dev_err(dev, "HW BCH selected, but BCH controller not found\n");
|
||||
dev_err(nfc->dev,
|
||||
"HW BCH selected, but BCH controller not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -179,15 +180,16 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
|
|||
chip->ecc.correct = jz4780_nand_ecc_correct;
|
||||
/* fall through */
|
||||
case NAND_ECC_SOFT:
|
||||
dev_info(dev, "using %s (strength %d, size %d, bytes %d)\n",
|
||||
(nfc->bch) ? "hardware BCH" : "software ECC",
|
||||
chip->ecc.strength, chip->ecc.size, chip->ecc.bytes);
|
||||
dev_info(nfc->dev, "using %s (strength %d, size %d, bytes %d)\n",
|
||||
(nfc->bch) ? "hardware BCH" : "software ECC",
|
||||
chip->ecc.strength, chip->ecc.size, chip->ecc.bytes);
|
||||
break;
|
||||
case NAND_ECC_NONE:
|
||||
dev_info(dev, "not using ECC\n");
|
||||
dev_info(nfc->dev, "not using ECC\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "ECC mode %d not supported\n", chip->ecc.mode);
|
||||
dev_err(nfc->dev, "ECC mode %d not supported\n",
|
||||
chip->ecc.mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -199,7 +201,7 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
|
|||
eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes;
|
||||
|
||||
if (eccbytes > mtd->oobsize - 2) {
|
||||
dev_err(dev,
|
||||
dev_err(nfc->dev,
|
||||
"invalid ECC config: required %d ECC bytes, but only %d are available",
|
||||
eccbytes, mtd->oobsize - 2);
|
||||
return -EINVAL;
|
||||
|
@ -210,6 +212,10 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops jz4780_nand_controller_ops = {
|
||||
.attach_chip = jz4780_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int jz4780_nand_init_chip(struct platform_device *pdev,
|
||||
struct jz4780_nand_controller *nfc,
|
||||
struct device_node *np,
|
||||
|
@ -279,15 +285,8 @@ static int jz4780_nand_init_chip(struct platform_device *pdev,
|
|||
chip->controller = &nfc->controller;
|
||||
nand_set_flash_node(chip, np);
|
||||
|
||||
ret = nand_scan_ident(mtd, 1, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = jz4780_nand_init_ecc(nand, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
chip->controller->ops = &jz4780_nand_controller_ops;
|
||||
ret = nand_scan(mtd, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -368,7 +367,7 @@ static int jz4780_nand_probe(struct platform_device *pdev)
|
|||
nfc->dev = dev;
|
||||
nfc->num_banks = num_banks;
|
||||
|
||||
nand_hw_control_init(&nfc->controller);
|
||||
nand_controller_init(&nfc->controller);
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
|
||||
ret = jz4780_nand_init_chips(nfc, pdev);
|
||||
|
|
|
@ -184,6 +184,7 @@ static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = {
|
|||
};
|
||||
|
||||
struct lpc32xx_nand_host {
|
||||
struct platform_device *pdev;
|
||||
struct nand_chip nand_chip;
|
||||
struct lpc32xx_mlc_platform_data *pdata;
|
||||
struct clk *clk;
|
||||
|
@ -653,6 +654,32 @@ static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev)
|
|||
return ncfg;
|
||||
}
|
||||
|
||||
static int lpc32xx_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
|
||||
struct device *dev = &host->pdev->dev;
|
||||
|
||||
host->dma_buf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL);
|
||||
if (!host->dma_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
host->dummy_buf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL);
|
||||
if (!host->dummy_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 512;
|
||||
mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
|
||||
host->mlcsubpages = mtd->writesize / 512;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops lpc32xx_nand_controller_ops = {
|
||||
.attach_chip = lpc32xx_nand_attach_chip,
|
||||
};
|
||||
|
||||
/*
|
||||
* Probe for NAND controller
|
||||
*/
|
||||
|
@ -669,6 +696,8 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
|||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
host->pdev = pdev;
|
||||
|
||||
rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->io_base = devm_ioremap_resource(&pdev->dev, rc);
|
||||
if (IS_ERR(host->io_base))
|
||||
|
@ -748,31 +777,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan to find existance of the device and
|
||||
* Get the type of NAND device SMALL block or LARGE block
|
||||
*/
|
||||
res = nand_scan_ident(mtd, 1, NULL);
|
||||
if (res)
|
||||
goto release_dma_chan;
|
||||
|
||||
host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL);
|
||||
if (!host->dma_buf) {
|
||||
res = -ENOMEM;
|
||||
goto release_dma_chan;
|
||||
}
|
||||
|
||||
host->dummy_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL);
|
||||
if (!host->dummy_buf) {
|
||||
res = -ENOMEM;
|
||||
goto release_dma_chan;
|
||||
}
|
||||
|
||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||
nand_chip->ecc.size = 512;
|
||||
mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
|
||||
host->mlcsubpages = mtd->writesize / 512;
|
||||
|
||||
/* initially clear interrupt status */
|
||||
readb(MLC_IRQ_SR(host->io_base));
|
||||
|
||||
|
@ -794,10 +798,11 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/*
|
||||
* Fills out all the uninitialized function pointers with the defaults
|
||||
* And scans for a bad block table if appropriate.
|
||||
* Scan to find existence of the device and get the type of NAND device:
|
||||
* SMALL block or LARGE block.
|
||||
*/
|
||||
res = nand_scan_tail(mtd);
|
||||
nand_chip->dummy_controller.ops = &lpc32xx_nand_controller_ops;
|
||||
res = nand_scan(mtd, 1);
|
||||
if (res)
|
||||
goto free_irq;
|
||||
|
||||
|
|
|
@ -779,6 +779,46 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev)
|
|||
return ncfg;
|
||||
}
|
||||
|
||||
static int lpc32xx_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
|
||||
|
||||
/* OOB and ECC CPU and DMA work areas */
|
||||
host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE);
|
||||
|
||||
/*
|
||||
* Small page FLASH has a unique OOB layout, but large and huge
|
||||
* page FLASH use the standard layout. Small page FLASH uses a
|
||||
* custom BBT marker layout.
|
||||
*/
|
||||
if (mtd->writesize <= 512)
|
||||
mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
|
||||
|
||||
/* These sizes remain the same regardless of page size */
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES;
|
||||
chip->ecc.prepad = 0;
|
||||
chip->ecc.postpad = 0;
|
||||
|
||||
/*
|
||||
* Use a custom BBT marker setup for small page FLASH that
|
||||
* won't interfere with the ECC layout. Large and huge page
|
||||
* FLASH use the standard layout.
|
||||
*/
|
||||
if ((chip->bbt_options & NAND_BBT_USE_FLASH) &&
|
||||
mtd->writesize <= 512) {
|
||||
chip->bbt_td = &bbt_smallpage_main_descr;
|
||||
chip->bbt_md = &bbt_smallpage_mirror_descr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops lpc32xx_nand_controller_ops = {
|
||||
.attach_chip = lpc32xx_nand_attach_chip,
|
||||
};
|
||||
|
||||
/*
|
||||
* Probe for NAND controller
|
||||
*/
|
||||
|
@ -884,41 +924,8 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Find NAND device */
|
||||
res = nand_scan_ident(mtd, 1, NULL);
|
||||
if (res)
|
||||
goto release_dma;
|
||||
|
||||
/* OOB and ECC CPU and DMA work areas */
|
||||
host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE);
|
||||
|
||||
/*
|
||||
* Small page FLASH has a unique OOB layout, but large and huge
|
||||
* page FLASH use the standard layout. Small page FLASH uses a
|
||||
* custom BBT marker layout.
|
||||
*/
|
||||
if (mtd->writesize <= 512)
|
||||
mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
|
||||
|
||||
/* These sizes remain the same regardless of page size */
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES;
|
||||
chip->ecc.prepad = chip->ecc.postpad = 0;
|
||||
|
||||
/*
|
||||
* Use a custom BBT marker setup for small page FLASH that
|
||||
* won't interfere with the ECC layout. Large and huge page
|
||||
* FLASH use the standard layout.
|
||||
*/
|
||||
if ((chip->bbt_options & NAND_BBT_USE_FLASH) &&
|
||||
mtd->writesize <= 512) {
|
||||
chip->bbt_td = &bbt_smallpage_main_descr;
|
||||
chip->bbt_md = &bbt_smallpage_mirror_descr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills out all the uninitialized function pointers with the defaults
|
||||
*/
|
||||
res = nand_scan_tail(mtd);
|
||||
chip->dummy_controller.ops = &lpc32xx_nand_controller_ops;
|
||||
res = nand_scan(mtd, 1);
|
||||
if (res)
|
||||
goto release_dma;
|
||||
|
||||
|
|
|
@ -318,7 +318,7 @@ struct marvell_nfc_caps {
|
|||
* @dma_buf: 32-bit aligned buffer for DMA transfers (NFCv1 only)
|
||||
*/
|
||||
struct marvell_nfc {
|
||||
struct nand_hw_control controller;
|
||||
struct nand_controller controller;
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *core_clk;
|
||||
|
@ -335,7 +335,7 @@ struct marvell_nfc {
|
|||
u8 *dma_buf;
|
||||
};
|
||||
|
||||
static inline struct marvell_nfc *to_marvell_nfc(struct nand_hw_control *ctrl)
|
||||
static inline struct marvell_nfc *to_marvell_nfc(struct nand_controller *ctrl)
|
||||
{
|
||||
return container_of(ctrl, struct marvell_nfc, controller);
|
||||
}
|
||||
|
@ -650,11 +650,6 @@ static void marvell_nfc_select_chip(struct mtd_info *mtd, int die_nr)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not change the timing registers when using the DT property
|
||||
* marvell,nand-keep-config; in that case ->ndtr0 and ->ndtr1 from the
|
||||
* marvell_nand structure are supposedly empty.
|
||||
*/
|
||||
writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0);
|
||||
writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1);
|
||||
|
||||
|
@ -2157,6 +2152,7 @@ static int marvell_nand_ecc_init(struct mtd_info *mtd,
|
|||
break;
|
||||
case NAND_ECC_NONE:
|
||||
case NAND_ECC_SOFT:
|
||||
case NAND_ECC_ON_DIE:
|
||||
if (!nfc->caps->is_nfcv2 && mtd->writesize != SZ_512 &&
|
||||
mtd->writesize != SZ_2K) {
|
||||
dev_err(nfc->dev, "NFCv1 cannot write %d bytes pages\n",
|
||||
|
@ -2294,6 +2290,111 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int marvell_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
|
||||
struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
|
||||
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(nfc->dev);
|
||||
int ret;
|
||||
|
||||
if (pdata && pdata->flash_bbt)
|
||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
/*
|
||||
* We'll use a bad block table stored in-flash and don't
|
||||
* allow writing the bad block marker to the flash.
|
||||
*/
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB_BBM;
|
||||
chip->bbt_td = &bbt_main_descr;
|
||||
chip->bbt_md = &bbt_mirror_descr;
|
||||
}
|
||||
|
||||
/* Save the chip-specific fields of NDCR */
|
||||
marvell_nand->ndcr = NDCR_PAGE_SZ(mtd->writesize);
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
marvell_nand->ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
|
||||
|
||||
/*
|
||||
* On small page NANDs, only one cycle is needed to pass the
|
||||
* column address.
|
||||
*/
|
||||
if (mtd->writesize <= 512) {
|
||||
marvell_nand->addr_cyc = 1;
|
||||
} else {
|
||||
marvell_nand->addr_cyc = 2;
|
||||
marvell_nand->ndcr |= NDCR_RA_START;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now add the number of cycles needed to pass the row
|
||||
* address.
|
||||
*
|
||||
* Addressing a chip using CS 2 or 3 should also need the third row
|
||||
* cycle but due to inconsistance in the documentation and lack of
|
||||
* hardware to test this situation, this case is not supported.
|
||||
*/
|
||||
if (chip->options & NAND_ROW_ADDR_3)
|
||||
marvell_nand->addr_cyc += 3;
|
||||
else
|
||||
marvell_nand->addr_cyc += 2;
|
||||
|
||||
if (pdata) {
|
||||
chip->ecc.size = pdata->ecc_step_size;
|
||||
chip->ecc.strength = pdata->ecc_strength;
|
||||
}
|
||||
|
||||
ret = marvell_nand_ecc_init(mtd, &chip->ecc);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "ECC init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_HW) {
|
||||
/*
|
||||
* Subpage write not available with hardware ECC, prohibit also
|
||||
* subpage read as in userspace subpage access would still be
|
||||
* allowed and subpage write, if used, would lead to numerous
|
||||
* uncorrectable ECC errors.
|
||||
*/
|
||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
}
|
||||
|
||||
if (pdata || nfc->caps->legacy_of_bindings) {
|
||||
/*
|
||||
* We keep the MTD name unchanged to avoid breaking platforms
|
||||
* where the MTD cmdline parser is used and the bootloader
|
||||
* has not been updated to use the new naming scheme.
|
||||
*/
|
||||
mtd->name = "pxa3xx_nand-0";
|
||||
} else if (!mtd->name) {
|
||||
/*
|
||||
* If the new bindings are used and the bootloader has not been
|
||||
* updated to pass a new mtdparts parameter on the cmdline, you
|
||||
* should define the following property in your NAND node, ie:
|
||||
*
|
||||
* label = "main-storage";
|
||||
*
|
||||
* This way, mtd->name will be set by the core when
|
||||
* nand_set_flash_node() is called.
|
||||
*/
|
||||
mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
|
||||
"%s:nand.%d", dev_name(nfc->dev),
|
||||
marvell_nand->sels[0].cs);
|
||||
if (!mtd->name) {
|
||||
dev_err(nfc->dev, "Failed to allocate mtd->name\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops marvell_nand_controller_ops = {
|
||||
.attach_chip = marvell_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
|
||||
struct device_node *np)
|
||||
{
|
||||
|
@ -2436,105 +2537,10 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
|
|||
marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1);
|
||||
|
||||
chip->options |= NAND_BUSWIDTH_AUTO;
|
||||
ret = nand_scan_ident(mtd, marvell_nand->nsels, NULL);
|
||||
|
||||
ret = nand_scan(mtd, marvell_nand->nsels);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not identify the nand chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pdata && pdata->flash_bbt)
|
||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
/*
|
||||
* We'll use a bad block table stored in-flash and don't
|
||||
* allow writing the bad block marker to the flash.
|
||||
*/
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB_BBM;
|
||||
chip->bbt_td = &bbt_main_descr;
|
||||
chip->bbt_md = &bbt_mirror_descr;
|
||||
}
|
||||
|
||||
/* Save the chip-specific fields of NDCR */
|
||||
marvell_nand->ndcr = NDCR_PAGE_SZ(mtd->writesize);
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
marvell_nand->ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
|
||||
|
||||
/*
|
||||
* On small page NANDs, only one cycle is needed to pass the
|
||||
* column address.
|
||||
*/
|
||||
if (mtd->writesize <= 512) {
|
||||
marvell_nand->addr_cyc = 1;
|
||||
} else {
|
||||
marvell_nand->addr_cyc = 2;
|
||||
marvell_nand->ndcr |= NDCR_RA_START;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now add the number of cycles needed to pass the row
|
||||
* address.
|
||||
*
|
||||
* Addressing a chip using CS 2 or 3 should also need the third row
|
||||
* cycle but due to inconsistance in the documentation and lack of
|
||||
* hardware to test this situation, this case is not supported.
|
||||
*/
|
||||
if (chip->options & NAND_ROW_ADDR_3)
|
||||
marvell_nand->addr_cyc += 3;
|
||||
else
|
||||
marvell_nand->addr_cyc += 2;
|
||||
|
||||
if (pdata) {
|
||||
chip->ecc.size = pdata->ecc_step_size;
|
||||
chip->ecc.strength = pdata->ecc_strength;
|
||||
}
|
||||
|
||||
ret = marvell_nand_ecc_init(mtd, &chip->ecc);
|
||||
if (ret) {
|
||||
dev_err(dev, "ECC init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_HW) {
|
||||
/*
|
||||
* Subpage write not available with hardware ECC, prohibit also
|
||||
* subpage read as in userspace subpage access would still be
|
||||
* allowed and subpage write, if used, would lead to numerous
|
||||
* uncorrectable ECC errors.
|
||||
*/
|
||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
}
|
||||
|
||||
if (pdata || nfc->caps->legacy_of_bindings) {
|
||||
/*
|
||||
* We keep the MTD name unchanged to avoid breaking platforms
|
||||
* where the MTD cmdline parser is used and the bootloader
|
||||
* has not been updated to use the new naming scheme.
|
||||
*/
|
||||
mtd->name = "pxa3xx_nand-0";
|
||||
} else if (!mtd->name) {
|
||||
/*
|
||||
* If the new bindings are used and the bootloader has not been
|
||||
* updated to pass a new mtdparts parameter on the cmdline, you
|
||||
* should define the following property in your NAND node, ie:
|
||||
*
|
||||
* label = "main-storage";
|
||||
*
|
||||
* This way, mtd->name will be set by the core when
|
||||
* nand_set_flash_node() is called.
|
||||
*/
|
||||
mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
|
||||
"%s:nand.%d", dev_name(nfc->dev),
|
||||
marvell_nand->sels[0].cs);
|
||||
if (!mtd->name) {
|
||||
dev_err(nfc->dev, "Failed to allocate mtd->name\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret) {
|
||||
dev_err(dev, "nand_scan_tail failed: %d\n", ret);
|
||||
dev_err(dev, "could not scan the nand chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2677,6 +2683,21 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void marvell_nfc_reset(struct marvell_nfc *nfc)
|
||||
{
|
||||
/*
|
||||
* ECC operations and interruptions are only enabled when specifically
|
||||
* needed. ECC shall not be activated in the early stages (fails probe).
|
||||
* Arbiter flag, even if marked as "reserved", must be set (empirical).
|
||||
* SPARE_EN bit must always be set or ECC bytes will not be at the same
|
||||
* offset in the read page and this will fail the protection.
|
||||
*/
|
||||
writel_relaxed(NDCR_ALL_INT | NDCR_ND_ARB_EN | NDCR_SPARE_EN |
|
||||
NDCR_RD_ID_CNT(NFCV1_READID_LEN), nfc->regs + NDCR);
|
||||
writel_relaxed(0xFFFFFFFF, nfc->regs + NDSR);
|
||||
writel_relaxed(0, nfc->regs + NDECCCTRL);
|
||||
}
|
||||
|
||||
static int marvell_nfc_init(struct marvell_nfc *nfc)
|
||||
{
|
||||
struct device_node *np = nfc->dev->of_node;
|
||||
|
@ -2715,17 +2736,7 @@ static int marvell_nfc_init(struct marvell_nfc *nfc)
|
|||
if (!nfc->caps->is_nfcv2)
|
||||
marvell_nfc_init_dma(nfc);
|
||||
|
||||
/*
|
||||
* ECC operations and interruptions are only enabled when specifically
|
||||
* needed. ECC shall not be activated in the early stages (fails probe).
|
||||
* Arbiter flag, even if marked as "reserved", must be set (empirical).
|
||||
* SPARE_EN bit must always be set or ECC bytes will not be at the same
|
||||
* offset in the read page and this will fail the protection.
|
||||
*/
|
||||
writel_relaxed(NDCR_ALL_INT | NDCR_ND_ARB_EN | NDCR_SPARE_EN |
|
||||
NDCR_RD_ID_CNT(NFCV1_READID_LEN), nfc->regs + NDCR);
|
||||
writel_relaxed(0xFFFFFFFF, nfc->regs + NDSR);
|
||||
writel_relaxed(0, nfc->regs + NDECCCTRL);
|
||||
marvell_nfc_reset(nfc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2744,7 +2755,8 @@ static int marvell_nfc_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
nfc->dev = dev;
|
||||
nand_hw_control_init(&nfc->controller);
|
||||
nand_controller_init(&nfc->controller);
|
||||
nfc->controller.ops = &marvell_nand_controller_ops;
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -2772,17 +2784,19 @@ static int marvell_nfc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
|
||||
nfc->reg_clk = devm_clk_get(&pdev->dev, "reg");
|
||||
if (PTR_ERR(nfc->reg_clk) != -ENOENT) {
|
||||
if (!IS_ERR(nfc->reg_clk)) {
|
||||
ret = clk_prepare_enable(nfc->reg_clk);
|
||||
if (ret)
|
||||
goto unprepare_core_clk;
|
||||
} else {
|
||||
if (IS_ERR(nfc->reg_clk)) {
|
||||
if (PTR_ERR(nfc->reg_clk) != -ENOENT) {
|
||||
ret = PTR_ERR(nfc->reg_clk);
|
||||
goto unprepare_core_clk;
|
||||
}
|
||||
|
||||
nfc->reg_clk = NULL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(nfc->reg_clk);
|
||||
if (ret)
|
||||
goto unprepare_core_clk;
|
||||
|
||||
marvell_nfc_disable_int(nfc, NDCR_ALL_INT);
|
||||
marvell_nfc_clear_int(nfc, NDCR_ALL_INT);
|
||||
ret = devm_request_irq(dev, irq, marvell_nfc_isr,
|
||||
|
@ -2840,6 +2854,49 @@ static int marvell_nfc_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused marvell_nfc_suspend(struct device *dev)
|
||||
{
|
||||
struct marvell_nfc *nfc = dev_get_drvdata(dev);
|
||||
struct marvell_nand_chip *chip;
|
||||
|
||||
list_for_each_entry(chip, &nfc->chips, node)
|
||||
marvell_nfc_wait_ndrun(&chip->chip);
|
||||
|
||||
clk_disable_unprepare(nfc->reg_clk);
|
||||
clk_disable_unprepare(nfc->core_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused marvell_nfc_resume(struct device *dev)
|
||||
{
|
||||
struct marvell_nfc *nfc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(nfc->core_clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(nfc->reg_clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Reset nfc->selected_chip so the next command will cause the timing
|
||||
* registers to be restored in marvell_nfc_select_chip().
|
||||
*/
|
||||
nfc->selected_chip = NULL;
|
||||
|
||||
/* Reset registers that have lost their contents */
|
||||
marvell_nfc_reset(nfc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops marvell_nfc_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(marvell_nfc_suspend, marvell_nfc_resume)
|
||||
};
|
||||
|
||||
static const struct marvell_nfc_caps marvell_armada_8k_nfc_caps = {
|
||||
.max_cs_nb = 4,
|
||||
.max_rb_nb = 2,
|
||||
|
@ -2924,6 +2981,7 @@ static struct platform_driver marvell_nfc_driver = {
|
|||
.driver = {
|
||||
.name = "marvell-nfc",
|
||||
.of_match_table = marvell_nfc_of_ids,
|
||||
.pm = &marvell_nfc_pm_ops,
|
||||
},
|
||||
.id_table = marvell_nfc_platform_ids,
|
||||
.probe = marvell_nfc_probe,
|
||||
|
|
|
@ -145,7 +145,7 @@ struct mtk_nfc_clk {
|
|||
};
|
||||
|
||||
struct mtk_nfc {
|
||||
struct nand_hw_control controller;
|
||||
struct nand_controller controller;
|
||||
struct mtk_ecc_config ecc_cfg;
|
||||
struct mtk_nfc_clk clk;
|
||||
struct mtk_ecc *ecc;
|
||||
|
@ -1250,13 +1250,54 @@ static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_nfc_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct device *dev = mtd->dev.parent;
|
||||
struct mtk_nfc *nfc = nand_get_controller_data(chip);
|
||||
struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
dev_err(dev, "16bits buswidth not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* store bbt magic in page, cause OOB is not protected */
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
ret = mtk_nfc_ecc_init(dev, mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtk_nfc_set_spare_per_sector(&mtk_nand->spare_per_sector, mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mtk_nfc_set_fdm(&mtk_nand->fdm, mtd);
|
||||
mtk_nfc_set_bad_mark_ctl(&mtk_nand->bad_mark, mtd);
|
||||
|
||||
len = mtd->writesize + mtd->oobsize;
|
||||
nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL);
|
||||
if (!nfc->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops mtk_nfc_controller_ops = {
|
||||
.attach_chip = mtk_nfc_attach_chip,
|
||||
};
|
||||
|
||||
static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct mtk_nfc_nand_chip *chip;
|
||||
struct nand_chip *nand;
|
||||
struct mtd_info *mtd;
|
||||
int nsels, len;
|
||||
int nsels;
|
||||
u32 tmp;
|
||||
int ret;
|
||||
int i;
|
||||
|
@ -1324,40 +1365,11 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
|
|||
|
||||
mtk_nfc_hw_init(nfc);
|
||||
|
||||
ret = nand_scan_ident(mtd, nsels, NULL);
|
||||
ret = nand_scan(mtd, nsels);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* store bbt magic in page, cause OOB is not protected */
|
||||
if (nand->bbt_options & NAND_BBT_USE_FLASH)
|
||||
nand->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
ret = mtk_nfc_ecc_init(dev, mtd);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
if (nand->options & NAND_BUSWIDTH_16) {
|
||||
dev_err(dev, "16bits buswidth not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mtk_nfc_set_fdm(&chip->fdm, mtd);
|
||||
mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, mtd);
|
||||
|
||||
len = mtd->writesize + mtd->oobsize;
|
||||
nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL);
|
||||
if (!nfc->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "mtd parse partition error\n");
|
||||
nand_release(mtd);
|
||||
|
@ -1443,6 +1455,7 @@ static int mtk_nfc_probe(struct platform_device *pdev)
|
|||
spin_lock_init(&nfc->controller.lock);
|
||||
init_waitqueue_head(&nfc->controller.wq);
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
nfc->controller.ops = &mtk_nfc_controller_ops;
|
||||
|
||||
/* probe defer if not ready */
|
||||
nfc->ecc = of_mtk_ecc_get(np);
|
||||
|
|
|
@ -1,20 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2008 Sascha Hauer, kernel@pengutronix.de
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
@ -34,8 +21,6 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <asm/mach/flash.h>
|
||||
#include <linux/platform_data/mtd-mxc_nand.h>
|
||||
|
||||
#define DRIVER_NAME "mxc_nand"
|
||||
|
@ -1686,7 +1671,7 @@ static const struct of_device_id mxcnd_dt_ids[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, mxcnd_dt_ids);
|
||||
|
||||
static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
static int mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
{
|
||||
struct device_node *np = host->dev->of_node;
|
||||
const struct of_device_id *of_id =
|
||||
|
@ -1700,12 +1685,80 @@ static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
|
|||
return 0;
|
||||
}
|
||||
#else
|
||||
static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
static int mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mxcnd_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct mxc_nand_host *host = nand_get_controller_data(chip);
|
||||
struct device *dev = mtd->dev.parent;
|
||||
|
||||
switch (chip->ecc.mode) {
|
||||
case NAND_ECC_HW:
|
||||
chip->ecc.read_page = mxc_nand_read_page;
|
||||
chip->ecc.read_page_raw = mxc_nand_read_page_raw;
|
||||
chip->ecc.read_oob = mxc_nand_read_oob;
|
||||
chip->ecc.write_page = mxc_nand_write_page_ecc;
|
||||
chip->ecc.write_page_raw = mxc_nand_write_page_raw;
|
||||
chip->ecc.write_oob = mxc_nand_write_oob;
|
||||
break;
|
||||
|
||||
case NAND_ECC_SOFT:
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
chip->bbt_td = &bbt_main_descr;
|
||||
chip->bbt_md = &bbt_mirror_descr;
|
||||
}
|
||||
|
||||
/* Allocate the right size buffer now */
|
||||
devm_kfree(dev, (void *)host->data_buf);
|
||||
host->data_buf = devm_kzalloc(dev, mtd->writesize + mtd->oobsize,
|
||||
GFP_KERNEL);
|
||||
if (!host->data_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Call preset again, with correct writesize chip time */
|
||||
host->devtype_data->preset(mtd);
|
||||
|
||||
if (!chip->ecc.bytes) {
|
||||
if (host->eccsize == 8)
|
||||
chip->ecc.bytes = 18;
|
||||
else if (host->eccsize == 4)
|
||||
chip->ecc.bytes = 9;
|
||||
}
|
||||
|
||||
/*
|
||||
* Experimentation shows that i.MX NFC can only handle up to 218 oob
|
||||
* bytes. Limit used_oobsize to 218 so as to not confuse copy_spare()
|
||||
* into copying invalid data to/from the spare IO buffer, as this
|
||||
* might cause ECC data corruption when doing sub-page write to a
|
||||
* partially written page.
|
||||
*/
|
||||
host->used_oobsize = min(mtd->oobsize, 218U);
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_HW) {
|
||||
if (is_imx21_nfc(host) || is_imx27_nfc(host))
|
||||
chip->ecc.strength = 1;
|
||||
else
|
||||
chip->ecc.strength = (host->eccsize == 4) ? 4 : 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops mxcnd_controller_ops = {
|
||||
.attach_chip = mxcnd_attach_chip,
|
||||
};
|
||||
|
||||
static int mxcnd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
|
@ -1845,71 +1898,9 @@ static int mxcnd_probe(struct platform_device *pdev)
|
|||
host->devtype_data->irq_control(host, 1);
|
||||
}
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
err = nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL);
|
||||
if (err)
|
||||
goto escan;
|
||||
|
||||
switch (this->ecc.mode) {
|
||||
case NAND_ECC_HW:
|
||||
this->ecc.read_page = mxc_nand_read_page;
|
||||
this->ecc.read_page_raw = mxc_nand_read_page_raw;
|
||||
this->ecc.read_oob = mxc_nand_read_oob;
|
||||
this->ecc.write_page = mxc_nand_write_page_ecc;
|
||||
this->ecc.write_page_raw = mxc_nand_write_page_raw;
|
||||
this->ecc.write_oob = mxc_nand_write_oob;
|
||||
break;
|
||||
|
||||
case NAND_ECC_SOFT:
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto escan;
|
||||
}
|
||||
|
||||
if (this->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
this->bbt_td = &bbt_main_descr;
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
}
|
||||
|
||||
/* allocate the right size buffer now */
|
||||
devm_kfree(&pdev->dev, (void *)host->data_buf);
|
||||
host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize,
|
||||
GFP_KERNEL);
|
||||
if (!host->data_buf) {
|
||||
err = -ENOMEM;
|
||||
goto escan;
|
||||
}
|
||||
|
||||
/* Call preset again, with correct writesize this time */
|
||||
host->devtype_data->preset(mtd);
|
||||
|
||||
if (!this->ecc.bytes) {
|
||||
if (host->eccsize == 8)
|
||||
this->ecc.bytes = 18;
|
||||
else if (host->eccsize == 4)
|
||||
this->ecc.bytes = 9;
|
||||
}
|
||||
|
||||
/*
|
||||
* Experimentation shows that i.MX NFC can only handle up to 218 oob
|
||||
* bytes. Limit used_oobsize to 218 so as to not confuse copy_spare()
|
||||
* into copying invalid data to/from the spare IO buffer, as this
|
||||
* might cause ECC data corruption when doing sub-page write to a
|
||||
* partially written page.
|
||||
*/
|
||||
host->used_oobsize = min(mtd->oobsize, 218U);
|
||||
|
||||
if (this->ecc.mode == NAND_ECC_HW) {
|
||||
if (is_imx21_nfc(host) || is_imx27_nfc(host))
|
||||
this->ecc.strength = 1;
|
||||
else
|
||||
this->ecc.strength = (host->eccsize == 4) ? 4 : 8;
|
||||
}
|
||||
|
||||
/* second phase scan */
|
||||
err = nand_scan_tail(mtd);
|
||||
/* Scan the NAND device */
|
||||
this->dummy_controller.ops = &mxcnd_controller_ops;
|
||||
err = nand_scan(mtd, is_imx25_nfc(host) ? 4 : 1);
|
||||
if (err)
|
||||
goto escan;
|
||||
|
||||
|
|
|
@ -2668,8 +2668,8 @@ static bool nand_subop_instr_is_valid(const struct nand_subop *subop,
|
|||
return subop && instr_idx < subop->ninstrs;
|
||||
}
|
||||
|
||||
static int nand_subop_get_start_off(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
static unsigned int nand_subop_get_start_off(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
{
|
||||
if (instr_idx)
|
||||
return 0;
|
||||
|
@ -2688,12 +2688,12 @@ static int nand_subop_get_start_off(const struct nand_subop *subop,
|
|||
*
|
||||
* Given an address instruction, returns the offset of the first cycle to issue.
|
||||
*/
|
||||
int nand_subop_get_addr_start_off(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
unsigned int nand_subop_get_addr_start_off(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
{
|
||||
if (!nand_subop_instr_is_valid(subop, instr_idx) ||
|
||||
subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)
|
||||
return -EINVAL;
|
||||
if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) ||
|
||||
subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR))
|
||||
return 0;
|
||||
|
||||
return nand_subop_get_start_off(subop, instr_idx);
|
||||
}
|
||||
|
@ -2710,14 +2710,14 @@ EXPORT_SYMBOL_GPL(nand_subop_get_addr_start_off);
|
|||
*
|
||||
* Given an address instruction, returns the number of address cycle to issue.
|
||||
*/
|
||||
int nand_subop_get_num_addr_cyc(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
unsigned int nand_subop_get_num_addr_cyc(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
{
|
||||
int start_off, end_off;
|
||||
|
||||
if (!nand_subop_instr_is_valid(subop, instr_idx) ||
|
||||
subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)
|
||||
return -EINVAL;
|
||||
if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) ||
|
||||
subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR))
|
||||
return 0;
|
||||
|
||||
start_off = nand_subop_get_addr_start_off(subop, instr_idx);
|
||||
|
||||
|
@ -2742,12 +2742,12 @@ EXPORT_SYMBOL_GPL(nand_subop_get_num_addr_cyc);
|
|||
*
|
||||
* Given a data instruction, returns the offset to start from.
|
||||
*/
|
||||
int nand_subop_get_data_start_off(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
unsigned int nand_subop_get_data_start_off(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
{
|
||||
if (!nand_subop_instr_is_valid(subop, instr_idx) ||
|
||||
!nand_instr_is_data(&subop->instrs[instr_idx]))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) ||
|
||||
!nand_instr_is_data(&subop->instrs[instr_idx])))
|
||||
return 0;
|
||||
|
||||
return nand_subop_get_start_off(subop, instr_idx);
|
||||
}
|
||||
|
@ -2764,14 +2764,14 @@ EXPORT_SYMBOL_GPL(nand_subop_get_data_start_off);
|
|||
*
|
||||
* Returns the length of the chunk of data to send/receive.
|
||||
*/
|
||||
int nand_subop_get_data_len(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
unsigned int nand_subop_get_data_len(const struct nand_subop *subop,
|
||||
unsigned int instr_idx)
|
||||
{
|
||||
int start_off = 0, end_off;
|
||||
|
||||
if (!nand_subop_instr_is_valid(subop, instr_idx) ||
|
||||
!nand_instr_is_data(&subop->instrs[instr_idx]))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) ||
|
||||
!nand_instr_is_data(&subop->instrs[instr_idx])))
|
||||
return 0;
|
||||
|
||||
start_off = nand_subop_get_data_start_off(subop, instr_idx);
|
||||
|
||||
|
@ -2966,6 +2966,23 @@ int nand_check_erased_ecc_chunk(void *data, int datalen,
|
|||
}
|
||||
EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
|
||||
|
||||
/**
|
||||
* nand_read_page_raw_notsupp - dummy read raw page function
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Returns -ENOTSUPP unconditionally.
|
||||
*/
|
||||
int nand_read_page_raw_notsupp(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
u8 *buf, int oob_required, int page)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_read_page_raw_notsupp);
|
||||
|
||||
/**
|
||||
* nand_read_page_raw - [INTERN] read raw page data without ecc
|
||||
* @mtd: mtd info structure
|
||||
|
@ -3960,6 +3977,22 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_write_page_raw_notsupp - dummy raw page write function
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: data buffer
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
* @page: page number to write
|
||||
*
|
||||
* Returns -ENOTSUPP unconditionally.
|
||||
*/
|
||||
int nand_write_page_raw_notsupp(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const u8 *buf, int oob_required, int page)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_write_page_raw_notsupp);
|
||||
|
||||
/**
|
||||
* nand_write_page_raw - [INTERN] raw page write function
|
||||
|
@ -4965,12 +4998,10 @@ static void nand_set_defaults(struct nand_chip *chip)
|
|||
chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
|
||||
if (!chip->read_buf || chip->read_buf == nand_read_buf)
|
||||
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
|
||||
if (!chip->scan_bbt)
|
||||
chip->scan_bbt = nand_default_bbt;
|
||||
|
||||
if (!chip->controller) {
|
||||
chip->controller = &chip->hwcontrol;
|
||||
nand_hw_control_init(chip->controller);
|
||||
chip->controller = &chip->dummy_controller;
|
||||
nand_controller_init(chip->controller);
|
||||
}
|
||||
|
||||
if (!chip->buf_align)
|
||||
|
@ -5120,6 +5151,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
|
|||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_onfi_params *p;
|
||||
struct onfi_params *onfi;
|
||||
int onfi_version = 0;
|
||||
char id[4];
|
||||
int i, ret, val;
|
||||
|
||||
|
@ -5168,30 +5201,35 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
|
|||
}
|
||||
}
|
||||
|
||||
if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
|
||||
chip->manufacturer.desc->ops->fixup_onfi_param_page)
|
||||
chip->manufacturer.desc->ops->fixup_onfi_param_page(chip, p);
|
||||
|
||||
/* Check version */
|
||||
val = le16_to_cpu(p->revision);
|
||||
if (val & (1 << 5))
|
||||
chip->parameters.onfi.version = 23;
|
||||
else if (val & (1 << 4))
|
||||
chip->parameters.onfi.version = 22;
|
||||
else if (val & (1 << 3))
|
||||
chip->parameters.onfi.version = 21;
|
||||
else if (val & (1 << 2))
|
||||
chip->parameters.onfi.version = 20;
|
||||
else if (val & (1 << 1))
|
||||
chip->parameters.onfi.version = 10;
|
||||
if (val & ONFI_VERSION_2_3)
|
||||
onfi_version = 23;
|
||||
else if (val & ONFI_VERSION_2_2)
|
||||
onfi_version = 22;
|
||||
else if (val & ONFI_VERSION_2_1)
|
||||
onfi_version = 21;
|
||||
else if (val & ONFI_VERSION_2_0)
|
||||
onfi_version = 20;
|
||||
else if (val & ONFI_VERSION_1_0)
|
||||
onfi_version = 10;
|
||||
|
||||
if (!chip->parameters.onfi.version) {
|
||||
if (!onfi_version) {
|
||||
pr_info("unsupported ONFI version: %d\n", val);
|
||||
goto free_onfi_param_page;
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
sanitize_string(p->manufacturer, sizeof(p->manufacturer));
|
||||
sanitize_string(p->model, sizeof(p->model));
|
||||
strncpy(chip->parameters.model, p->model,
|
||||
sizeof(chip->parameters.model) - 1);
|
||||
chip->parameters.model = kstrdup(p->model, GFP_KERNEL);
|
||||
if (!chip->parameters.model) {
|
||||
ret = -ENOMEM;
|
||||
goto free_onfi_param_page;
|
||||
}
|
||||
|
||||
mtd->writesize = le32_to_cpu(p->byte_per_page);
|
||||
|
||||
|
@ -5219,7 +5257,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
|
|||
if (p->ecc_bits != 0xff) {
|
||||
chip->ecc_strength_ds = p->ecc_bits;
|
||||
chip->ecc_step_ds = 512;
|
||||
} else if (chip->parameters.onfi.version >= 21 &&
|
||||
} else if (onfi_version >= 21 &&
|
||||
(le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
|
||||
|
||||
/*
|
||||
|
@ -5246,19 +5284,33 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
|
|||
bitmap_set(chip->parameters.set_feature_list,
|
||||
ONFI_FEATURE_ADDR_TIMING_MODE, 1);
|
||||
}
|
||||
chip->parameters.onfi.tPROG = le16_to_cpu(p->t_prog);
|
||||
chip->parameters.onfi.tBERS = le16_to_cpu(p->t_bers);
|
||||
chip->parameters.onfi.tR = le16_to_cpu(p->t_r);
|
||||
chip->parameters.onfi.tCCS = le16_to_cpu(p->t_ccs);
|
||||
chip->parameters.onfi.async_timing_mode =
|
||||
le16_to_cpu(p->async_timing_mode);
|
||||
chip->parameters.onfi.vendor_revision =
|
||||
le16_to_cpu(p->vendor_revision);
|
||||
memcpy(chip->parameters.onfi.vendor, p->vendor,
|
||||
sizeof(p->vendor));
|
||||
|
||||
onfi = kzalloc(sizeof(*onfi), GFP_KERNEL);
|
||||
if (!onfi) {
|
||||
ret = -ENOMEM;
|
||||
goto free_model;
|
||||
}
|
||||
|
||||
onfi->version = onfi_version;
|
||||
onfi->tPROG = le16_to_cpu(p->t_prog);
|
||||
onfi->tBERS = le16_to_cpu(p->t_bers);
|
||||
onfi->tR = le16_to_cpu(p->t_r);
|
||||
onfi->tCCS = le16_to_cpu(p->t_ccs);
|
||||
onfi->async_timing_mode = le16_to_cpu(p->async_timing_mode);
|
||||
onfi->vendor_revision = le16_to_cpu(p->vendor_revision);
|
||||
memcpy(onfi->vendor, p->vendor, sizeof(p->vendor));
|
||||
chip->parameters.onfi = onfi;
|
||||
|
||||
/* Identification done, free the full ONFI parameter page and exit */
|
||||
kfree(p);
|
||||
|
||||
return 1;
|
||||
|
||||
free_model:
|
||||
kfree(chip->parameters.model);
|
||||
free_onfi_param_page:
|
||||
kfree(p);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -5321,8 +5373,11 @@ static int nand_flash_detect_jedec(struct nand_chip *chip)
|
|||
|
||||
sanitize_string(p->manufacturer, sizeof(p->manufacturer));
|
||||
sanitize_string(p->model, sizeof(p->model));
|
||||
strncpy(chip->parameters.model, p->model,
|
||||
sizeof(chip->parameters.model) - 1);
|
||||
chip->parameters.model = kstrdup(p->model, GFP_KERNEL);
|
||||
if (!chip->parameters.model) {
|
||||
ret = -ENOMEM;
|
||||
goto free_jedec_param_page;
|
||||
}
|
||||
|
||||
mtd->writesize = le32_to_cpu(p->byte_per_page);
|
||||
|
||||
|
@ -5511,8 +5566,9 @@ static bool find_full_id_nand(struct nand_chip *chip,
|
|||
chip->onfi_timing_mode_default =
|
||||
type->onfi_timing_mode_default;
|
||||
|
||||
strncpy(chip->parameters.model, type->name,
|
||||
sizeof(chip->parameters.model) - 1);
|
||||
chip->parameters.model = kstrdup(type->name, GFP_KERNEL);
|
||||
if (!chip->parameters.model)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -5651,7 +5707,6 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
|
|||
}
|
||||
}
|
||||
|
||||
chip->parameters.onfi.version = 0;
|
||||
if (!type->name || !type->pagesize) {
|
||||
/* Check if the chip is ONFI compliant */
|
||||
ret = nand_flash_detect_onfi(chip);
|
||||
|
@ -5671,8 +5726,9 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
|
|||
if (!type->name)
|
||||
return -ENODEV;
|
||||
|
||||
strncpy(chip->parameters.model, type->name,
|
||||
sizeof(chip->parameters.model) - 1);
|
||||
chip->parameters.model = kstrdup(type->name, GFP_KERNEL);
|
||||
if (!chip->parameters.model)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||
|
||||
|
@ -5702,7 +5758,9 @@ ident_done:
|
|||
mtd->name);
|
||||
pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8,
|
||||
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
|
||||
goto free_detect_allocation;
|
||||
}
|
||||
|
||||
nand_decode_bbm_options(chip);
|
||||
|
@ -5739,6 +5797,11 @@ ident_done:
|
|||
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
|
||||
mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
|
||||
return 0;
|
||||
|
||||
free_detect_allocation:
|
||||
kfree(chip->parameters.model);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const nand_ecc_modes[] = {
|
||||
|
@ -5777,6 +5840,7 @@ static int of_get_nand_ecc_mode(struct device_node *np)
|
|||
static const char * const nand_ecc_algos[] = {
|
||||
[NAND_ECC_HAMMING] = "hamming",
|
||||
[NAND_ECC_BCH] = "bch",
|
||||
[NAND_ECC_RS] = "rs",
|
||||
};
|
||||
|
||||
static int of_get_nand_ecc_algo(struct device_node *np)
|
||||
|
@ -5858,6 +5922,9 @@ static int nand_dt_init(struct nand_chip *chip)
|
|||
if (of_get_nand_bus_width(dn) == 16)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (of_property_read_bool(dn, "nand-is-boot-medium"))
|
||||
chip->options |= NAND_IS_BOOT_MEDIUM;
|
||||
|
||||
if (of_get_nand_on_flash_bbt(dn))
|
||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
|
||||
|
@ -5885,7 +5952,7 @@ static int nand_dt_init(struct nand_chip *chip)
|
|||
}
|
||||
|
||||
/**
|
||||
* nand_scan_ident - [NAND Interface] Scan for the NAND device
|
||||
* nand_scan_ident - Scan for the NAND device
|
||||
* @mtd: MTD device structure
|
||||
* @maxchips: number of chips to scan for
|
||||
* @table: alternative NAND ID table
|
||||
|
@ -5893,9 +5960,13 @@ static int nand_dt_init(struct nand_chip *chip)
|
|||
* This is the first phase of the normal nand_scan() function. It reads the
|
||||
* flash ID and sets up MTD fields accordingly.
|
||||
*
|
||||
* This helper used to be called directly from controller drivers that needed
|
||||
* to tweak some ECC-related parameters before nand_scan_tail(). This separation
|
||||
* prevented dynamic allocations during this phase which was unconvenient and
|
||||
* as been banned for the benefit of the ->init_ecc()/cleanup_ecc() hooks.
|
||||
*/
|
||||
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
struct nand_flash_dev *table)
|
||||
static int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
struct nand_flash_dev *table)
|
||||
{
|
||||
int i, nand_maf_id, nand_dev_id;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
@ -5969,7 +6040,12 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_scan_ident);
|
||||
|
||||
static void nand_scan_ident_cleanup(struct nand_chip *chip)
|
||||
{
|
||||
kfree(chip->parameters.model);
|
||||
kfree(chip->parameters.onfi);
|
||||
}
|
||||
|
||||
static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
|
||||
{
|
||||
|
@ -6077,24 +6153,17 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
|
|||
* by the controller and the calculated ECC bytes fit within the chip's OOB.
|
||||
* On success, the calculated ECC bytes is set.
|
||||
*/
|
||||
int nand_check_ecc_caps(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
static int
|
||||
nand_check_ecc_caps(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
int preset_step = chip->ecc.size;
|
||||
int preset_strength = chip->ecc.strength;
|
||||
int nsteps, ecc_bytes;
|
||||
int ecc_bytes, nsteps = mtd->writesize / preset_step;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON(oobavail < 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (!preset_step || !preset_strength)
|
||||
return -ENODATA;
|
||||
|
||||
nsteps = mtd->writesize / preset_step;
|
||||
|
||||
for (i = 0; i < caps->nstepinfos; i++) {
|
||||
stepinfo = &caps->stepinfos[i];
|
||||
|
||||
|
@ -6127,7 +6196,6 @@ int nand_check_ecc_caps(struct nand_chip *chip,
|
|||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
|
||||
|
||||
/**
|
||||
* nand_match_ecc_req - meet the chip's requirement with least ECC bytes
|
||||
|
@ -6139,8 +6207,9 @@ EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
|
|||
* number of ECC bytes (i.e. with the largest number of OOB-free bytes).
|
||||
* On success, the chosen ECC settings are set.
|
||||
*/
|
||||
int nand_match_ecc_req(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
static int
|
||||
nand_match_ecc_req(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
|
@ -6151,9 +6220,6 @@ int nand_match_ecc_req(struct nand_chip *chip,
|
|||
int best_ecc_bytes_total = INT_MAX;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON(oobavail < 0))
|
||||
return -EINVAL;
|
||||
|
||||
/* No information provided by the NAND chip */
|
||||
if (!req_step || !req_strength)
|
||||
return -ENOTSUPP;
|
||||
|
@ -6212,7 +6278,6 @@ int nand_match_ecc_req(struct nand_chip *chip,
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_match_ecc_req);
|
||||
|
||||
/**
|
||||
* nand_maximize_ecc - choose the max ECC strength available
|
||||
|
@ -6223,8 +6288,9 @@ EXPORT_SYMBOL_GPL(nand_match_ecc_req);
|
|||
* Choose the max ECC strength that is supported on the controller, and can fit
|
||||
* within the chip's OOB. On success, the chosen ECC settings are set.
|
||||
*/
|
||||
int nand_maximize_ecc(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
static int
|
||||
nand_maximize_ecc(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
|
@ -6234,9 +6300,6 @@ int nand_maximize_ecc(struct nand_chip *chip,
|
|||
int best_strength, best_ecc_bytes;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON(oobavail < 0))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < caps->nstepinfos; i++) {
|
||||
stepinfo = &caps->stepinfos[i];
|
||||
step_size = stepinfo->stepsize;
|
||||
|
@ -6285,7 +6348,44 @@ int nand_maximize_ecc(struct nand_chip *chip,
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_maximize_ecc);
|
||||
|
||||
/**
|
||||
* nand_ecc_choose_conf - Set the ECC strength and ECC step size
|
||||
* @chip: nand chip info structure
|
||||
* @caps: ECC engine caps info structure
|
||||
* @oobavail: OOB size that the ECC engine can use
|
||||
*
|
||||
* Choose the ECC configuration according to following logic
|
||||
*
|
||||
* 1. If both ECC step size and ECC strength are already set (usually by DT)
|
||||
* then check if it is supported by this controller.
|
||||
* 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength.
|
||||
* 3. Otherwise, try to match the ECC step size and ECC strength closest
|
||||
* to the chip's requirement. If available OOB size can't fit the chip
|
||||
* requirement then fallback to the maximum ECC step size and ECC strength.
|
||||
*
|
||||
* On success, the chosen ECC settings are set.
|
||||
*/
|
||||
int nand_ecc_choose_conf(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
if (WARN_ON(oobavail < 0 || oobavail > mtd->oobsize))
|
||||
return -EINVAL;
|
||||
|
||||
if (chip->ecc.size && chip->ecc.strength)
|
||||
return nand_check_ecc_caps(chip, caps, oobavail);
|
||||
|
||||
if (chip->ecc.options & NAND_ECC_MAXIMIZE)
|
||||
return nand_maximize_ecc(chip, caps, oobavail);
|
||||
|
||||
if (!nand_match_ecc_req(chip, caps, oobavail))
|
||||
return 0;
|
||||
|
||||
return nand_maximize_ecc(chip, caps, oobavail);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_ecc_choose_conf);
|
||||
|
||||
/*
|
||||
* Check if the chip configuration meet the datasheet requirements.
|
||||
|
@ -6322,14 +6422,14 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
|
|||
}
|
||||
|
||||
/**
|
||||
* nand_scan_tail - [NAND Interface] Scan for the NAND device
|
||||
* nand_scan_tail - Scan for the NAND device
|
||||
* @mtd: MTD device structure
|
||||
*
|
||||
* This is the second phase of the normal nand_scan() function. It fills out
|
||||
* all the uninitialized function pointers with the defaults and scans for a
|
||||
* bad block table if appropriate.
|
||||
*/
|
||||
int nand_scan_tail(struct mtd_info *mtd)
|
||||
static int nand_scan_tail(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
@ -6636,7 +6736,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
return 0;
|
||||
|
||||
/* Build bad block table */
|
||||
ret = chip->scan_bbt(mtd);
|
||||
ret = nand_create_bbt(chip);
|
||||
if (ret)
|
||||
goto err_nand_manuf_cleanup;
|
||||
|
||||
|
@ -6653,24 +6753,27 @@ err_free_buf:
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_scan_tail);
|
||||
|
||||
/*
|
||||
* is_module_text_address() isn't exported, and it's mostly a pointless
|
||||
* test if this is a module _anyway_ -- they'd have to try _really_ hard
|
||||
* to call us from in-kernel code if the core NAND support is modular.
|
||||
*/
|
||||
#ifdef MODULE
|
||||
#define caller_is_module() (1)
|
||||
#else
|
||||
#define caller_is_module() \
|
||||
is_module_text_address((unsigned long)__builtin_return_address(0))
|
||||
#endif
|
||||
static int nand_attach(struct nand_chip *chip)
|
||||
{
|
||||
if (chip->controller->ops && chip->controller->ops->attach_chip)
|
||||
return chip->controller->ops->attach_chip(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nand_detach(struct nand_chip *chip)
|
||||
{
|
||||
if (chip->controller->ops && chip->controller->ops->detach_chip)
|
||||
chip->controller->ops->detach_chip(chip);
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_scan_with_ids - [NAND Interface] Scan for the NAND device
|
||||
* @mtd: MTD device structure
|
||||
* @maxchips: number of chips to scan for
|
||||
* @maxchips: number of chips to scan for. @nand_scan_ident() will not be run if
|
||||
* this parameter is zero (useful for specific drivers that must
|
||||
* handle this part of the process themselves, e.g docg4).
|
||||
* @ids: optional flash IDs table
|
||||
*
|
||||
* This fills out all the uninitialized function pointers with the defaults.
|
||||
|
@ -6680,11 +6783,30 @@ EXPORT_SYMBOL(nand_scan_tail);
|
|||
int nand_scan_with_ids(struct mtd_info *mtd, int maxchips,
|
||||
struct nand_flash_dev *ids)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
int ret;
|
||||
|
||||
ret = nand_scan_ident(mtd, maxchips, ids);
|
||||
if (!ret)
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (maxchips) {
|
||||
ret = nand_scan_ident(mtd, maxchips, ids);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nand_attach(chip);
|
||||
if (ret)
|
||||
goto cleanup_ident;
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret)
|
||||
goto detach_chip;
|
||||
|
||||
return 0;
|
||||
|
||||
detach_chip:
|
||||
nand_detach(chip);
|
||||
cleanup_ident:
|
||||
nand_scan_ident_cleanup(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_scan_with_ids);
|
||||
|
@ -6712,7 +6834,14 @@ void nand_cleanup(struct nand_chip *chip)
|
|||
|
||||
/* Free manufacturer priv data. */
|
||||
nand_manufacturer_cleanup(chip);
|
||||
|
||||
/* Free controller specific allocations after chip identification */
|
||||
nand_detach(chip);
|
||||
|
||||
/* Free identification phase allocations */
|
||||
nand_scan_ident_cleanup(chip);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(nand_cleanup);
|
||||
|
||||
/**
|
||||
|
|
|
@ -1349,15 +1349,14 @@ static int nand_create_badblock_pattern(struct nand_chip *this)
|
|||
}
|
||||
|
||||
/**
|
||||
* nand_default_bbt - [NAND Interface] Select a default bad block table for the device
|
||||
* @mtd: MTD device structure
|
||||
* nand_create_bbt - [NAND Interface] Select a default bad block table for the device
|
||||
* @this: NAND chip object
|
||||
*
|
||||
* This function selects the default bad block table support for the device and
|
||||
* calls the nand_scan_bbt function.
|
||||
*/
|
||||
int nand_default_bbt(struct mtd_info *mtd)
|
||||
int nand_create_bbt(struct nand_chip *this)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
int ret;
|
||||
|
||||
/* Is a flash based bad block table requested? */
|
||||
|
@ -1383,8 +1382,9 @@ int nand_default_bbt(struct mtd_info *mtd)
|
|||
return ret;
|
||||
}
|
||||
|
||||
return nand_scan_bbt(mtd, this->badblock_pattern);
|
||||
return nand_scan_bbt(nand_to_mtd(this), this->badblock_pattern);
|
||||
}
|
||||
EXPORT_SYMBOL(nand_create_bbt);
|
||||
|
||||
/**
|
||||
* nand_isreserved_bbt - [NAND Interface] Check if a block is reserved
|
||||
|
|
|
@ -100,6 +100,16 @@ static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val)
|
|||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u16 column = ((u16)addr << 8) | addr;
|
||||
|
||||
if (chip->exec_op) {
|
||||
struct nand_op_instr instrs[] = {
|
||||
NAND_OP_ADDR(1, &addr, 0),
|
||||
NAND_OP_8BIT_DATA_OUT(1, &val, 0),
|
||||
};
|
||||
struct nand_operation op = NAND_OPERATION(instrs);
|
||||
|
||||
return nand_exec_op(chip, &op);
|
||||
}
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1);
|
||||
chip->write_byte(mtd, val);
|
||||
|
||||
|
@ -473,6 +483,19 @@ static void hynix_nand_extract_oobsize(struct nand_chip *chip,
|
|||
WARN(1, "Invalid OOB size");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The datasheet of H27UCG8T2BTR mentions that the "Redundant
|
||||
* Area Size" is encoded "per 8KB" (page size). This chip uses
|
||||
* a page size of 16KiB. The datasheet mentions an OOB size of
|
||||
* 1.280 bytes, but the OOB size encoded in the ID bytes (using
|
||||
* the existing logic above) is 640 bytes.
|
||||
* Update the OOB size for this chip by taking the value
|
||||
* determined above and scaling it to the actual page size (so
|
||||
* the actual OOB size for this chip is: 640 * 16k / 8k).
|
||||
*/
|
||||
if (chip->id.data[1] == 0xde)
|
||||
mtd->oobsize *= mtd->writesize / SZ_8K;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,33 @@
|
|||
*/
|
||||
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Special Micron status bit that indicates when the block has been
|
||||
* corrected by on-die ECC and should be rewritten
|
||||
* Special Micron status bit 3 indicates that the block has been
|
||||
* corrected by on-die ECC and should be rewritten.
|
||||
*/
|
||||
#define NAND_STATUS_WRITE_RECOMMENDED BIT(3)
|
||||
#define NAND_ECC_STATUS_WRITE_RECOMMENDED BIT(3)
|
||||
|
||||
/*
|
||||
* On chips with 8-bit ECC and additional bit can be used to distinguish
|
||||
* cases where a errors were corrected without needing a rewrite
|
||||
*
|
||||
* Bit 4 Bit 3 Bit 0 Description
|
||||
* ----- ----- ----- -----------
|
||||
* 0 0 0 No Errors
|
||||
* 0 0 1 Multiple uncorrected errors
|
||||
* 0 1 0 4 - 6 errors corrected, recommend rewrite
|
||||
* 0 1 1 Reserved
|
||||
* 1 0 0 1 - 3 errors corrected
|
||||
* 1 0 1 Reserved
|
||||
* 1 1 0 7 - 8 errors corrected, recommend rewrite
|
||||
*/
|
||||
#define NAND_ECC_STATUS_MASK (BIT(4) | BIT(3) | BIT(0))
|
||||
#define NAND_ECC_STATUS_UNCORRECTABLE BIT(0)
|
||||
#define NAND_ECC_STATUS_4_6_CORRECTED BIT(3)
|
||||
#define NAND_ECC_STATUS_1_3_CORRECTED BIT(4)
|
||||
#define NAND_ECC_STATUS_7_8_CORRECTED (BIT(4) | BIT(3))
|
||||
|
||||
struct nand_onfi_vendor_micron {
|
||||
u8 two_plane_read;
|
||||
|
@ -43,6 +64,16 @@ struct nand_onfi_vendor_micron {
|
|||
u8 param_revision;
|
||||
} __packed;
|
||||
|
||||
struct micron_on_die_ecc {
|
||||
bool forced;
|
||||
bool enabled;
|
||||
void *rawbuf;
|
||||
};
|
||||
|
||||
struct micron_nand {
|
||||
struct micron_on_die_ecc ecc;
|
||||
};
|
||||
|
||||
static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
@ -57,9 +88,10 @@ static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
|
|||
static int micron_nand_onfi_init(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_parameters *p = &chip->parameters;
|
||||
struct nand_onfi_vendor_micron *micron = (void *)p->onfi.vendor;
|
||||
|
||||
if (chip->parameters.onfi.version && p->onfi.vendor_revision) {
|
||||
if (p->onfi) {
|
||||
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;
|
||||
}
|
||||
|
@ -74,8 +106,9 @@ static int micron_nand_onfi_init(struct nand_chip *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int micron_nand_on_die_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
static int micron_nand_on_die_4_ooblayout_ecc(struct mtd_info *mtd,
|
||||
int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
if (section >= 4)
|
||||
return -ERANGE;
|
||||
|
@ -86,8 +119,9 @@ static int micron_nand_on_die_ooblayout_ecc(struct mtd_info *mtd, int section,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
static int micron_nand_on_die_4_ooblayout_free(struct mtd_info *mtd,
|
||||
int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
if (section >= 4)
|
||||
return -ERANGE;
|
||||
|
@ -98,19 +132,161 @@ static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops micron_nand_on_die_ooblayout_ops = {
|
||||
.ecc = micron_nand_on_die_ooblayout_ecc,
|
||||
.free = micron_nand_on_die_ooblayout_free,
|
||||
static const struct mtd_ooblayout_ops micron_nand_on_die_4_ooblayout_ops = {
|
||||
.ecc = micron_nand_on_die_4_ooblayout_ecc,
|
||||
.free = micron_nand_on_die_4_ooblayout_free,
|
||||
};
|
||||
|
||||
static int micron_nand_on_die_8_ooblayout_ecc(struct mtd_info *mtd,
|
||||
int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->offset = mtd->oobsize - chip->ecc.total;
|
||||
oobregion->length = chip->ecc.total;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int micron_nand_on_die_8_ooblayout_free(struct mtd_info *mtd,
|
||||
int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->offset = 2;
|
||||
oobregion->length = mtd->oobsize - chip->ecc.total - 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops micron_nand_on_die_8_ooblayout_ops = {
|
||||
.ecc = micron_nand_on_die_8_ooblayout_ecc,
|
||||
.free = micron_nand_on_die_8_ooblayout_free,
|
||||
};
|
||||
|
||||
static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable)
|
||||
{
|
||||
struct micron_nand *micron = nand_get_manufacturer_data(chip);
|
||||
u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
|
||||
int ret;
|
||||
|
||||
if (micron->ecc.forced)
|
||||
return 0;
|
||||
|
||||
if (micron->ecc.enabled == enable)
|
||||
return 0;
|
||||
|
||||
if (enable)
|
||||
feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN;
|
||||
|
||||
return nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
|
||||
ret = nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
|
||||
if (!ret)
|
||||
micron->ecc.enabled = enable;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int micron_nand_on_die_ecc_status_4(struct nand_chip *chip, u8 status,
|
||||
void *buf, int page,
|
||||
int oob_required)
|
||||
{
|
||||
struct micron_nand *micron = nand_get_manufacturer_data(chip);
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
unsigned int step, max_bitflips = 0;
|
||||
int ret;
|
||||
|
||||
if (!(status & NAND_ECC_STATUS_WRITE_RECOMMENDED)) {
|
||||
if (status & NAND_STATUS_FAIL)
|
||||
mtd->ecc_stats.failed++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The internal ECC doesn't tell us the number of bitflips that have
|
||||
* been corrected, but tells us if it recommends to rewrite the block.
|
||||
* If it's the case, we need to read the page in raw mode and compare
|
||||
* its content to the corrected version to extract the actual number of
|
||||
* bitflips.
|
||||
* But before we do that, we must make sure we have all OOB bytes read
|
||||
* in non-raw mode, even if the user did not request those bytes.
|
||||
*/
|
||||
if (!oob_required) {
|
||||
ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize,
|
||||
false);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
micron_nand_on_die_ecc_setup(chip, false);
|
||||
|
||||
ret = nand_read_page_op(chip, page, 0, micron->ecc.rawbuf,
|
||||
mtd->writesize + mtd->oobsize);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (step = 0; step < chip->ecc.steps; step++) {
|
||||
unsigned int offs, i, nbitflips = 0;
|
||||
u8 *rawbuf, *corrbuf;
|
||||
|
||||
offs = step * chip->ecc.size;
|
||||
rawbuf = micron->ecc.rawbuf + offs;
|
||||
corrbuf = buf + offs;
|
||||
|
||||
for (i = 0; i < chip->ecc.size; i++)
|
||||
nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]);
|
||||
|
||||
offs = (step * 16) + 4;
|
||||
rawbuf = micron->ecc.rawbuf + mtd->writesize + offs;
|
||||
corrbuf = chip->oob_poi + offs;
|
||||
|
||||
for (i = 0; i < chip->ecc.bytes + 4; i++)
|
||||
nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]);
|
||||
|
||||
if (WARN_ON(nbitflips > chip->ecc.strength))
|
||||
return -EINVAL;
|
||||
|
||||
max_bitflips = max(nbitflips, max_bitflips);
|
||||
mtd->ecc_stats.corrected += nbitflips;
|
||||
}
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
static int micron_nand_on_die_ecc_status_8(struct nand_chip *chip, u8 status)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
/*
|
||||
* With 8/512 we have more information but still don't know precisely
|
||||
* how many bit-flips were seen.
|
||||
*/
|
||||
switch (status & NAND_ECC_STATUS_MASK) {
|
||||
case NAND_ECC_STATUS_UNCORRECTABLE:
|
||||
mtd->ecc_stats.failed++;
|
||||
return 0;
|
||||
case NAND_ECC_STATUS_1_3_CORRECTED:
|
||||
mtd->ecc_stats.corrected += 3;
|
||||
return 3;
|
||||
case NAND_ECC_STATUS_4_6_CORRECTED:
|
||||
mtd->ecc_stats.corrected += 6;
|
||||
/* rewrite recommended */
|
||||
return 6;
|
||||
case NAND_ECC_STATUS_7_8_CORRECTED:
|
||||
mtd->ecc_stats.corrected += 8;
|
||||
/* rewrite recommended */
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -137,24 +313,18 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (status & NAND_STATUS_FAIL)
|
||||
mtd->ecc_stats.failed++;
|
||||
|
||||
/*
|
||||
* The internal ECC doesn't tell us the number of bitflips
|
||||
* that have been corrected, but tells us if it recommends to
|
||||
* rewrite the block. If it's the case, then we pretend we had
|
||||
* a number of bitflips equal to the ECC strength, which will
|
||||
* hint the NAND core to rewrite the block.
|
||||
*/
|
||||
else if (status & NAND_STATUS_WRITE_RECOMMENDED)
|
||||
max_bitflips = chip->ecc.strength;
|
||||
|
||||
ret = nand_read_data_op(chip, buf, mtd->writesize, false);
|
||||
if (!ret && oob_required)
|
||||
ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize,
|
||||
false);
|
||||
|
||||
if (chip->ecc.strength == 4)
|
||||
max_bitflips = micron_nand_on_die_ecc_status_4(chip, status,
|
||||
buf, page,
|
||||
oob_required);
|
||||
else
|
||||
max_bitflips = micron_nand_on_die_ecc_status_8(chip, status);
|
||||
|
||||
out:
|
||||
micron_nand_on_die_ecc_setup(chip, false);
|
||||
|
||||
|
@ -195,6 +365,9 @@ enum {
|
|||
MICRON_ON_DIE_MANDATORY,
|
||||
};
|
||||
|
||||
#define MICRON_ID_INTERNAL_ECC_MASK GENMASK(1, 0)
|
||||
#define MICRON_ID_ECC_ENABLED BIT(7)
|
||||
|
||||
/*
|
||||
* Try to detect if the NAND support on-die ECC. To do this, we enable
|
||||
* the feature, and read back if it has been enabled as expected. We
|
||||
|
@ -207,42 +380,52 @@ enum {
|
|||
*/
|
||||
static int micron_supports_on_die_ecc(struct nand_chip *chip)
|
||||
{
|
||||
u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
|
||||
u8 id[5];
|
||||
int ret;
|
||||
|
||||
if (!chip->parameters.onfi.version)
|
||||
if (!chip->parameters.onfi)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
if (chip->bits_per_cell != 1)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
/*
|
||||
* We only support on-die ECC of 4/512 or 8/512
|
||||
*/
|
||||
if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
/* 0x2 means on-die ECC is available. */
|
||||
if (chip->id.len != 5 ||
|
||||
(chip->id.data[4] & MICRON_ID_INTERNAL_ECC_MASK) != 0x2)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
ret = micron_nand_on_die_ecc_setup(chip, true);
|
||||
if (ret)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = nand_readid_op(chip, 0, id, sizeof(id));
|
||||
if (ret)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0)
|
||||
if (!(id[4] & MICRON_ID_ECC_ENABLED))
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
ret = micron_nand_on_die_ecc_setup(chip, false);
|
||||
if (ret)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = nand_readid_op(chip, 0, id, sizeof(id));
|
||||
if (ret)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN)
|
||||
if (id[4] & MICRON_ID_ECC_ENABLED)
|
||||
return MICRON_ON_DIE_MANDATORY;
|
||||
|
||||
/*
|
||||
* Some Micron NANDs have an on-die ECC of 4/512, some other
|
||||
* 8/512. We only support the former.
|
||||
* We only support on-die ECC of 4/512 or 8/512
|
||||
*/
|
||||
if (chip->ecc_strength_ds != 4)
|
||||
if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8)
|
||||
return MICRON_ON_DIE_UNSUPPORTED;
|
||||
|
||||
return MICRON_ON_DIE_SUPPORTED;
|
||||
|
@ -251,44 +434,116 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
|
|||
static int micron_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct micron_nand *micron;
|
||||
int ondie;
|
||||
int ret;
|
||||
|
||||
micron = kzalloc(sizeof(*micron), GFP_KERNEL);
|
||||
if (!micron)
|
||||
return -ENOMEM;
|
||||
|
||||
nand_set_manufacturer_data(chip, micron);
|
||||
|
||||
ret = micron_nand_onfi_init(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_free_manuf_data;
|
||||
|
||||
if (mtd->writesize == 2048)
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
|
||||
ondie = micron_supports_on_die_ecc(chip);
|
||||
|
||||
if (ondie == MICRON_ON_DIE_MANDATORY) {
|
||||
if (ondie == MICRON_ON_DIE_MANDATORY &&
|
||||
chip->ecc.mode != NAND_ECC_ON_DIE) {
|
||||
pr_err("On-die ECC forcefully enabled, not supported\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err_free_manuf_data;
|
||||
}
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_ON_DIE) {
|
||||
if (ondie == MICRON_ON_DIE_UNSUPPORTED) {
|
||||
pr_err("On-die ECC selected but not supported\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err_free_manuf_data;
|
||||
}
|
||||
|
||||
chip->ecc.bytes = 8;
|
||||
if (ondie == MICRON_ON_DIE_MANDATORY) {
|
||||
micron->ecc.forced = true;
|
||||
micron->ecc.enabled = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case of 4bit on-die ECC, we need a buffer to store a
|
||||
* page dumped in raw mode so that we can compare its content
|
||||
* to the same page after ECC correction happened and extract
|
||||
* the real number of bitflips from this comparison.
|
||||
* That's not needed for 8-bit ECC, because the status expose
|
||||
* a better approximation of the number of bitflips in a page.
|
||||
*/
|
||||
if (chip->ecc_strength_ds == 4) {
|
||||
micron->ecc.rawbuf = kmalloc(mtd->writesize +
|
||||
mtd->oobsize,
|
||||
GFP_KERNEL);
|
||||
if (!micron->ecc.rawbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_manuf_data;
|
||||
}
|
||||
}
|
||||
|
||||
if (chip->ecc_strength_ds == 4)
|
||||
mtd_set_ooblayout(mtd,
|
||||
µn_nand_on_die_4_ooblayout_ops);
|
||||
else
|
||||
mtd_set_ooblayout(mtd,
|
||||
µn_nand_on_die_8_ooblayout_ops);
|
||||
|
||||
chip->ecc.bytes = chip->ecc_strength_ds * 2;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.strength = 4;
|
||||
chip->ecc.strength = chip->ecc_strength_ds;
|
||||
chip->ecc.algo = NAND_ECC_BCH;
|
||||
chip->ecc.read_page = micron_nand_read_page_on_die_ecc;
|
||||
chip->ecc.write_page = micron_nand_write_page_on_die_ecc;
|
||||
chip->ecc.read_page_raw = nand_read_page_raw;
|
||||
chip->ecc.write_page_raw = nand_write_page_raw;
|
||||
|
||||
mtd_set_ooblayout(mtd, µn_nand_on_die_ooblayout_ops);
|
||||
if (ondie == MICRON_ON_DIE_MANDATORY) {
|
||||
chip->ecc.read_page_raw = nand_read_page_raw_notsupp;
|
||||
chip->ecc.write_page_raw = nand_write_page_raw_notsupp;
|
||||
} else {
|
||||
chip->ecc.read_page_raw = nand_read_page_raw;
|
||||
chip->ecc.write_page_raw = nand_write_page_raw;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_manuf_data:
|
||||
kfree(micron->ecc.rawbuf);
|
||||
kfree(micron);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void micron_nand_cleanup(struct nand_chip *chip)
|
||||
{
|
||||
struct micron_nand *micron = nand_get_manufacturer_data(chip);
|
||||
|
||||
kfree(micron->ecc.rawbuf);
|
||||
kfree(micron);
|
||||
}
|
||||
|
||||
static void micron_fixup_onfi_param_page(struct nand_chip *chip,
|
||||
struct nand_onfi_params *p)
|
||||
{
|
||||
/*
|
||||
* MT29F1G08ABAFAWP-ITE:F and possibly others report 00 00 for the
|
||||
* revision number field of the ONFI parameter page. Assume ONFI
|
||||
* version 1.0 if the revision number is 00 00.
|
||||
*/
|
||||
if (le16_to_cpu(p->revision) == 0)
|
||||
p->revision = cpu_to_le16(ONFI_VERSION_1_0);
|
||||
}
|
||||
|
||||
const struct nand_manufacturer_ops micron_nand_manuf_ops = {
|
||||
.init = micron_nand_init,
|
||||
.cleanup = micron_nand_cleanup,
|
||||
.fixup_onfi_param_page = micron_fixup_onfi_param_page,
|
||||
};
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
|
||||
#define ONFI_DYN_TIMING_MAX U16_MAX
|
||||
|
||||
static const struct nand_data_interface onfi_sdr_timings[] = {
|
||||
/* Mode 0 */
|
||||
{
|
||||
|
@ -292,6 +294,7 @@ int onfi_fill_data_interface(struct nand_chip *chip,
|
|||
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;
|
||||
|
@ -303,20 +306,35 @@ int onfi_fill_data_interface(struct nand_chip *chip,
|
|||
|
||||
/*
|
||||
* Initialize timings that cannot be deduced from timing mode:
|
||||
* tR, tPROG, tCCS, ...
|
||||
* tPROG, tBERS, tR and tCCS.
|
||||
* These information are part of the ONFI parameter page.
|
||||
*/
|
||||
if (chip->parameters.onfi.version) {
|
||||
struct nand_parameters *params = &chip->parameters;
|
||||
if (onfi) {
|
||||
struct nand_sdr_timings *timings = &iface->timings.sdr;
|
||||
|
||||
/* microseconds -> picoseconds */
|
||||
timings->tPROG_max = 1000000ULL * params->onfi.tPROG;
|
||||
timings->tBERS_max = 1000000ULL * params->onfi.tBERS;
|
||||
timings->tR_max = 1000000ULL * params->onfi.tR;
|
||||
timings->tPROG_max = 1000000ULL * onfi->tPROG;
|
||||
timings->tBERS_max = 1000000ULL * onfi->tBERS;
|
||||
timings->tR_max = 1000000ULL * onfi->tR;
|
||||
|
||||
/* nanoseconds -> picoseconds */
|
||||
timings->tCCS_min = 1000UL * params->onfi.tCCS;
|
||||
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 = 1000000ULL * 200000000ULL;
|
||||
|
||||
/* nanoseconds -> picoseconds */
|
||||
timings->tCCS_min = 1000UL * 500000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -2192,6 +2192,48 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
|||
return;
|
||||
}
|
||||
|
||||
static int ns_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
unsigned int eccsteps, eccbytes;
|
||||
|
||||
if (!bch)
|
||||
return 0;
|
||||
|
||||
if (!mtd_nand_has_bch()) {
|
||||
NS_ERR("BCH ECC support is disabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Use 512-byte ecc blocks */
|
||||
eccsteps = nsmtd->writesize / 512;
|
||||
eccbytes = ((bch * 13) + 7) / 8;
|
||||
|
||||
/* Do not bother supporting small page devices */
|
||||
if (nsmtd->oobsize < 64 || !eccsteps) {
|
||||
NS_ERR("BCH not available on small page devices\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (((eccbytes * eccsteps) + 2) > nsmtd->oobsize) {
|
||||
NS_ERR("Invalid BCH value %u\n", bch);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->ecc.algo = NAND_ECC_BCH;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.strength = bch;
|
||||
chip->ecc.bytes = eccbytes;
|
||||
|
||||
NS_INFO("Using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops ns_controller_ops = {
|
||||
.attach_chip = ns_attach_chip,
|
||||
};
|
||||
|
||||
/*
|
||||
* Module initialization function
|
||||
*/
|
||||
|
@ -2276,44 +2318,10 @@ static int __init ns_init_module(void)
|
|||
if ((retval = parse_gravepages()) != 0)
|
||||
goto error;
|
||||
|
||||
retval = nand_scan_ident(nsmtd, 1, NULL);
|
||||
chip->dummy_controller.ops = &ns_controller_ops;
|
||||
retval = nand_scan(nsmtd, 1);
|
||||
if (retval) {
|
||||
NS_ERR("cannot scan NAND Simulator device\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (bch) {
|
||||
unsigned int eccsteps, eccbytes;
|
||||
if (!mtd_nand_has_bch()) {
|
||||
NS_ERR("BCH ECC support is disabled\n");
|
||||
retval = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
/* use 512-byte ecc blocks */
|
||||
eccsteps = nsmtd->writesize/512;
|
||||
eccbytes = (bch*13+7)/8;
|
||||
/* do not bother supporting small page devices */
|
||||
if ((nsmtd->oobsize < 64) || !eccsteps) {
|
||||
NS_ERR("bch not available on small page devices\n");
|
||||
retval = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if ((eccbytes*eccsteps+2) > nsmtd->oobsize) {
|
||||
NS_ERR("invalid bch value %u\n", bch);
|
||||
retval = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->ecc.algo = NAND_ECC_BCH;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.strength = bch;
|
||||
chip->ecc.bytes = eccbytes;
|
||||
NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size);
|
||||
}
|
||||
|
||||
retval = nand_scan_tail(nsmtd);
|
||||
if (retval) {
|
||||
NS_ERR("can't register NAND Simulator\n");
|
||||
NS_ERR("Could not scan NAND Simulator device\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -2337,7 +2345,7 @@ static int __init ns_init_module(void)
|
|||
if ((retval = init_nandsim(nsmtd)) != 0)
|
||||
goto err_exit;
|
||||
|
||||
if ((retval = chip->scan_bbt(nsmtd)) != 0)
|
||||
if ((retval = nand_create_bbt(chip)) != 0)
|
||||
goto err_exit;
|
||||
|
||||
if ((retval = parse_badblocks(nand, nsmtd)) != 0)
|
||||
|
|
|
@ -39,7 +39,7 @@ struct ndfc_controller {
|
|||
void __iomem *ndfcbase;
|
||||
struct nand_chip chip;
|
||||
int chip_select;
|
||||
struct nand_hw_control ndfc_control;
|
||||
struct nand_controller ndfc_control;
|
||||
};
|
||||
|
||||
static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS];
|
||||
|
@ -218,7 +218,7 @@ static int ndfc_probe(struct platform_device *ofdev)
|
|||
ndfc = &ndfc_ctrl[cs];
|
||||
ndfc->chip_select = cs;
|
||||
|
||||
nand_hw_control_init(&ndfc->ndfc_control);
|
||||
nand_controller_init(&ndfc->ndfc_control);
|
||||
ndfc->ofdev = ofdev;
|
||||
dev_set_drvdata(&ofdev->dev, ndfc);
|
||||
|
||||
|
|
|
@ -144,12 +144,6 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
|
|||
0xac, 0x6b, 0xff, 0x99, 0x7b};
|
||||
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
|
||||
|
||||
/* Shared among all NAND instances to synchronize access to the ECC Engine */
|
||||
static struct nand_hw_control omap_gpmc_controller = {
|
||||
.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
|
||||
.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
|
||||
};
|
||||
|
||||
struct omap_nand_info {
|
||||
struct nand_chip nand;
|
||||
struct platform_device *pdev;
|
||||
|
@ -1915,17 +1909,278 @@ static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
|
|||
.free = omap_sw_ooblayout_free,
|
||||
};
|
||||
|
||||
static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
struct device *dev = &info->pdev->dev;
|
||||
int min_oobbytes = BADBLOCK_MARKER_LENGTH;
|
||||
int oobbytes_per_step;
|
||||
dma_cap_mask_t mask;
|
||||
int err;
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
else
|
||||
chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
/* Re-populate low-level callbacks based on xfer modes */
|
||||
switch (info->xfer_type) {
|
||||
case NAND_OMAP_PREFETCH_POLLED:
|
||||
chip->read_buf = omap_read_buf_pref;
|
||||
chip->write_buf = omap_write_buf_pref;
|
||||
break;
|
||||
|
||||
case NAND_OMAP_POLLED:
|
||||
/* Use nand_base defaults for {read,write}_buf */
|
||||
break;
|
||||
|
||||
case NAND_OMAP_PREFETCH_DMA:
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
info->dma = dma_request_chan(dev, "rxtx");
|
||||
|
||||
if (IS_ERR(info->dma)) {
|
||||
dev_err(dev, "DMA engine request failed\n");
|
||||
return PTR_ERR(info->dma);
|
||||
} else {
|
||||
struct dma_slave_config cfg;
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.src_addr = info->phys_base;
|
||||
cfg.dst_addr = info->phys_base;
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.src_maxburst = 16;
|
||||
cfg.dst_maxburst = 16;
|
||||
err = dmaengine_slave_config(info->dma, &cfg);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"DMA engine slave config failed: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
chip->read_buf = omap_read_buf_dma_pref;
|
||||
chip->write_buf = omap_write_buf_dma_pref;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAND_OMAP_PREFETCH_IRQ:
|
||||
info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0);
|
||||
if (info->gpmc_irq_fifo <= 0) {
|
||||
dev_err(dev, "Error getting fifo IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
err = devm_request_irq(dev, info->gpmc_irq_fifo,
|
||||
omap_nand_irq, IRQF_SHARED,
|
||||
"gpmc-nand-fifo", info);
|
||||
if (err) {
|
||||
dev_err(dev, "Requesting IRQ %d, error %d\n",
|
||||
info->gpmc_irq_fifo, err);
|
||||
info->gpmc_irq_fifo = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
info->gpmc_irq_count = platform_get_irq(info->pdev, 1);
|
||||
if (info->gpmc_irq_count <= 0) {
|
||||
dev_err(dev, "Error getting IRQ count\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
err = devm_request_irq(dev, info->gpmc_irq_count,
|
||||
omap_nand_irq, IRQF_SHARED,
|
||||
"gpmc-nand-count", info);
|
||||
if (err) {
|
||||
dev_err(dev, "Requesting IRQ %d, error %d\n",
|
||||
info->gpmc_irq_count, err);
|
||||
info->gpmc_irq_count = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
chip->read_buf = omap_read_buf_irq_pref;
|
||||
chip->write_buf = omap_write_buf_irq_pref;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!omap2_nand_ecc_check(info))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Bail out earlier to let NAND_ECC_SOFT code create its own
|
||||
* ooblayout instead of using ours.
|
||||
*/
|
||||
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->ecc.algo = NAND_ECC_HAMMING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Populate MTD interface based on ECC scheme */
|
||||
switch (info->ecc_opt) {
|
||||
case OMAP_ECC_HAM1_CODE_HW:
|
||||
dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n");
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.strength = 1;
|
||||
chip->ecc.calculate = omap_calculate_ecc;
|
||||
chip->ecc.hwctl = omap_enable_hwecc;
|
||||
chip->ecc.correct = omap_correct_data;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = chip->ecc.bytes;
|
||||
|
||||
if (!(chip->options & NAND_BUSWIDTH_16))
|
||||
min_oobbytes = 1;
|
||||
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
|
||||
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 7;
|
||||
chip->ecc.strength = 4;
|
||||
chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
chip->ecc.correct = nand_bch_correct_data;
|
||||
chip->ecc.calculate = omap_calculate_ecc_bch_sw;
|
||||
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
|
||||
/* Reserve one byte for the OMAP marker */
|
||||
oobbytes_per_step = chip->ecc.bytes + 1;
|
||||
/* Software BCH library is used for locating errors */
|
||||
chip->ecc.priv = nand_bch_init(mtd);
|
||||
if (!chip->ecc.priv) {
|
||||
dev_err(dev, "Unable to use BCH library\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH4_CODE_HW:
|
||||
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 512;
|
||||
/* 14th bit is kept reserved for ROM-code compatibility */
|
||||
chip->ecc.bytes = 7 + 1;
|
||||
chip->ecc.strength = 4;
|
||||
chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
chip->ecc.correct = omap_elm_correct_data;
|
||||
chip->ecc.read_page = omap_read_page_bch;
|
||||
chip->ecc.write_page = omap_write_page_bch;
|
||||
chip->ecc.write_subpage = omap_write_subpage_bch;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, BCH4_ECC,
|
||||
mtd->writesize / chip->ecc.size,
|
||||
chip->ecc.size, chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 13;
|
||||
chip->ecc.strength = 8;
|
||||
chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
chip->ecc.correct = nand_bch_correct_data;
|
||||
chip->ecc.calculate = omap_calculate_ecc_bch_sw;
|
||||
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
|
||||
/* Reserve one byte for the OMAP marker */
|
||||
oobbytes_per_step = chip->ecc.bytes + 1;
|
||||
/* Software BCH library is used for locating errors */
|
||||
chip->ecc.priv = nand_bch_init(mtd);
|
||||
if (!chip->ecc.priv) {
|
||||
dev_err(dev, "unable to use BCH library\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 512;
|
||||
/* 14th bit is kept reserved for ROM-code compatibility */
|
||||
chip->ecc.bytes = 13 + 1;
|
||||
chip->ecc.strength = 8;
|
||||
chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
chip->ecc.correct = omap_elm_correct_data;
|
||||
chip->ecc.read_page = omap_read_page_bch;
|
||||
chip->ecc.write_page = omap_write_page_bch;
|
||||
chip->ecc.write_subpage = omap_write_subpage_bch;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, BCH8_ECC,
|
||||
mtd->writesize / chip->ecc.size,
|
||||
chip->ecc.size, chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 26;
|
||||
chip->ecc.strength = 16;
|
||||
chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
chip->ecc.correct = omap_elm_correct_data;
|
||||
chip->ecc.read_page = omap_read_page_bch;
|
||||
chip->ecc.write_page = omap_write_page_bch;
|
||||
chip->ecc.write_subpage = omap_write_subpage_bch;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, BCH16_ECC,
|
||||
mtd->writesize / chip->ecc.size,
|
||||
chip->ecc.size, chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid or unsupported ECC scheme\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check if NAND device's OOB is enough to store ECC signatures */
|
||||
min_oobbytes += (oobbytes_per_step *
|
||||
(mtd->writesize / chip->ecc.size));
|
||||
if (mtd->oobsize < min_oobbytes) {
|
||||
dev_err(dev,
|
||||
"Not enough OOB bytes: required = %d, available=%d\n",
|
||||
min_oobbytes, mtd->oobsize);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops omap_nand_controller_ops = {
|
||||
.attach_chip = omap_nand_attach_chip,
|
||||
};
|
||||
|
||||
/* Shared among all NAND instances to synchronize access to the ECC Engine */
|
||||
static struct nand_controller omap_gpmc_controller = {
|
||||
.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
|
||||
.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
|
||||
.ops = &omap_nand_controller_ops,
|
||||
};
|
||||
|
||||
static int omap_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_nand_info *info;
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *nand_chip;
|
||||
int err;
|
||||
dma_cap_mask_t mask;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
int min_oobbytes = BADBLOCK_MARKER_LENGTH;
|
||||
int oobbytes_per_step;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
|
||||
GFP_KERNEL);
|
||||
|
@ -1998,266 +2253,8 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
|
||||
/* scan NAND device connected to chip controller */
|
||||
nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
|
||||
err = nand_scan_ident(mtd, 1, NULL);
|
||||
if (err) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"scan failed, may be bus-width mismatch\n");
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
nand_chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
else
|
||||
nand_chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
/* re-populate low-level callbacks based on xfer modes */
|
||||
switch (info->xfer_type) {
|
||||
case NAND_OMAP_PREFETCH_POLLED:
|
||||
nand_chip->read_buf = omap_read_buf_pref;
|
||||
nand_chip->write_buf = omap_write_buf_pref;
|
||||
break;
|
||||
|
||||
case NAND_OMAP_POLLED:
|
||||
/* Use nand_base defaults for {read,write}_buf */
|
||||
break;
|
||||
|
||||
case NAND_OMAP_PREFETCH_DMA:
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
info->dma = dma_request_chan(pdev->dev.parent, "rxtx");
|
||||
|
||||
if (IS_ERR(info->dma)) {
|
||||
dev_err(&pdev->dev, "DMA engine request failed\n");
|
||||
err = PTR_ERR(info->dma);
|
||||
goto return_error;
|
||||
} else {
|
||||
struct dma_slave_config cfg;
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.src_addr = info->phys_base;
|
||||
cfg.dst_addr = info->phys_base;
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.src_maxburst = 16;
|
||||
cfg.dst_maxburst = 16;
|
||||
err = dmaengine_slave_config(info->dma, &cfg);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
|
||||
err);
|
||||
goto return_error;
|
||||
}
|
||||
nand_chip->read_buf = omap_read_buf_dma_pref;
|
||||
nand_chip->write_buf = omap_write_buf_dma_pref;
|
||||
}
|
||||
break;
|
||||
|
||||
case NAND_OMAP_PREFETCH_IRQ:
|
||||
info->gpmc_irq_fifo = platform_get_irq(pdev, 0);
|
||||
if (info->gpmc_irq_fifo <= 0) {
|
||||
dev_err(&pdev->dev, "error getting fifo irq\n");
|
||||
err = -ENODEV;
|
||||
goto return_error;
|
||||
}
|
||||
err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo,
|
||||
omap_nand_irq, IRQF_SHARED,
|
||||
"gpmc-nand-fifo", info);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "requesting irq(%d) error:%d",
|
||||
info->gpmc_irq_fifo, err);
|
||||
info->gpmc_irq_fifo = 0;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
info->gpmc_irq_count = platform_get_irq(pdev, 1);
|
||||
if (info->gpmc_irq_count <= 0) {
|
||||
dev_err(&pdev->dev, "error getting count irq\n");
|
||||
err = -ENODEV;
|
||||
goto return_error;
|
||||
}
|
||||
err = devm_request_irq(&pdev->dev, info->gpmc_irq_count,
|
||||
omap_nand_irq, IRQF_SHARED,
|
||||
"gpmc-nand-count", info);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "requesting irq(%d) error:%d",
|
||||
info->gpmc_irq_count, err);
|
||||
info->gpmc_irq_count = 0;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
nand_chip->read_buf = omap_read_buf_irq_pref;
|
||||
nand_chip->write_buf = omap_write_buf_irq_pref;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&pdev->dev,
|
||||
"xfer_type(%d) not supported!\n", info->xfer_type);
|
||||
err = -EINVAL;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
if (!omap2_nand_ecc_check(info)) {
|
||||
err = -EINVAL;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bail out earlier to let NAND_ECC_SOFT code create its own
|
||||
* ooblayout instead of using ours.
|
||||
*/
|
||||
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
|
||||
nand_chip->ecc.mode = NAND_ECC_SOFT;
|
||||
nand_chip->ecc.algo = NAND_ECC_HAMMING;
|
||||
goto scan_tail;
|
||||
}
|
||||
|
||||
/* populate MTD interface based on ECC scheme */
|
||||
switch (info->ecc_opt) {
|
||||
case OMAP_ECC_HAM1_CODE_HW:
|
||||
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
|
||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||
nand_chip->ecc.bytes = 3;
|
||||
nand_chip->ecc.size = 512;
|
||||
nand_chip->ecc.strength = 1;
|
||||
nand_chip->ecc.calculate = omap_calculate_ecc;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc;
|
||||
nand_chip->ecc.correct = omap_correct_data;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = nand_chip->ecc.bytes;
|
||||
|
||||
if (!(nand_chip->options & NAND_BUSWIDTH_16))
|
||||
min_oobbytes = 1;
|
||||
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
|
||||
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
|
||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||
nand_chip->ecc.size = 512;
|
||||
nand_chip->ecc.bytes = 7;
|
||||
nand_chip->ecc.strength = 4;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = nand_bch_correct_data;
|
||||
nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw;
|
||||
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
|
||||
/* Reserve one byte for the OMAP marker */
|
||||
oobbytes_per_step = nand_chip->ecc.bytes + 1;
|
||||
/* software bch library is used for locating errors */
|
||||
nand_chip->ecc.priv = nand_bch_init(mtd);
|
||||
if (!nand_chip->ecc.priv) {
|
||||
dev_err(&info->pdev->dev, "unable to use BCH library\n");
|
||||
err = -EINVAL;
|
||||
goto return_error;
|
||||
}
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH4_CODE_HW:
|
||||
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
|
||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||
nand_chip->ecc.size = 512;
|
||||
/* 14th bit is kept reserved for ROM-code compatibility */
|
||||
nand_chip->ecc.bytes = 7 + 1;
|
||||
nand_chip->ecc.strength = 4;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = omap_elm_correct_data;
|
||||
nand_chip->ecc.read_page = omap_read_page_bch;
|
||||
nand_chip->ecc.write_page = omap_write_page_bch;
|
||||
nand_chip->ecc.write_subpage = omap_write_subpage_bch;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = nand_chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, BCH4_ECC,
|
||||
mtd->writesize / nand_chip->ecc.size,
|
||||
nand_chip->ecc.size, nand_chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
goto return_error;
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
|
||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||
nand_chip->ecc.size = 512;
|
||||
nand_chip->ecc.bytes = 13;
|
||||
nand_chip->ecc.strength = 8;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = nand_bch_correct_data;
|
||||
nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw;
|
||||
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
|
||||
/* Reserve one byte for the OMAP marker */
|
||||
oobbytes_per_step = nand_chip->ecc.bytes + 1;
|
||||
/* software bch library is used for locating errors */
|
||||
nand_chip->ecc.priv = nand_bch_init(mtd);
|
||||
if (!nand_chip->ecc.priv) {
|
||||
dev_err(&info->pdev->dev, "unable to use BCH library\n");
|
||||
err = -EINVAL;
|
||||
goto return_error;
|
||||
}
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH8_CODE_HW:
|
||||
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
|
||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||
nand_chip->ecc.size = 512;
|
||||
/* 14th bit is kept reserved for ROM-code compatibility */
|
||||
nand_chip->ecc.bytes = 13 + 1;
|
||||
nand_chip->ecc.strength = 8;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = omap_elm_correct_data;
|
||||
nand_chip->ecc.read_page = omap_read_page_bch;
|
||||
nand_chip->ecc.write_page = omap_write_page_bch;
|
||||
nand_chip->ecc.write_subpage = omap_write_subpage_bch;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = nand_chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, BCH8_ECC,
|
||||
mtd->writesize / nand_chip->ecc.size,
|
||||
nand_chip->ecc.size, nand_chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
goto return_error;
|
||||
|
||||
break;
|
||||
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
|
||||
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||
nand_chip->ecc.size = 512;
|
||||
nand_chip->ecc.bytes = 26;
|
||||
nand_chip->ecc.strength = 16;
|
||||
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||
nand_chip->ecc.correct = omap_elm_correct_data;
|
||||
nand_chip->ecc.read_page = omap_read_page_bch;
|
||||
nand_chip->ecc.write_page = omap_write_page_bch;
|
||||
nand_chip->ecc.write_subpage = omap_write_subpage_bch;
|
||||
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
||||
oobbytes_per_step = nand_chip->ecc.bytes;
|
||||
|
||||
err = elm_config(info->elm_dev, BCH16_ECC,
|
||||
mtd->writesize / nand_chip->ecc.size,
|
||||
nand_chip->ecc.size, nand_chip->ecc.bytes);
|
||||
if (err < 0)
|
||||
goto return_error;
|
||||
|
||||
break;
|
||||
default:
|
||||
dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
|
||||
err = -EINVAL;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
/* check if NAND device's OOB is enough to store ECC signatures */
|
||||
min_oobbytes += (oobbytes_per_step *
|
||||
(mtd->writesize / nand_chip->ecc.size));
|
||||
if (mtd->oobsize < min_oobbytes) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"not enough OOB bytes required = %d, available=%d\n",
|
||||
min_oobbytes, mtd->oobsize);
|
||||
err = -EINVAL;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
scan_tail:
|
||||
/* second phase scan */
|
||||
err = nand_scan_tail(mtd);
|
||||
err = nand_scan(mtd, 1);
|
||||
if (err)
|
||||
goto return_error;
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/sizes.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/platform_data/mtd-orion_nand.h>
|
||||
|
||||
struct orion_nand_info {
|
||||
|
@ -52,7 +52,7 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
void __iomem *io_base = chip->IO_ADDR_R;
|
||||
#if __LINUX_ARM_ARCH__ >= 5
|
||||
#if defined(__LINUX_ARM_ARCH__) && __LINUX_ARM_ARCH__ >= 5
|
||||
uint64_t *buf64;
|
||||
#endif
|
||||
int i = 0;
|
||||
|
@ -61,7 +61,7 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|||
*buf++ = readb(io_base);
|
||||
len--;
|
||||
}
|
||||
#if __LINUX_ARM_ARCH__ >= 5
|
||||
#if defined(__LINUX_ARM_ARCH__) && __LINUX_ARM_ARCH__ >= 5
|
||||
buf64 = (uint64_t *)buf;
|
||||
while (i < len/8) {
|
||||
/*
|
||||
|
@ -153,9 +153,6 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
|||
if (board->width == 16)
|
||||
nc->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (board->dev_ready)
|
||||
nc->dev_ready = board->dev_ready;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
/* Not all platforms can gate the clock, so it is not
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#define OXNAS_NAND_MAX_CHIPS 1
|
||||
|
||||
struct oxnas_nand_ctrl {
|
||||
struct nand_hw_control base;
|
||||
struct nand_controller base;
|
||||
void __iomem *io_base;
|
||||
struct clk *clk;
|
||||
struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS];
|
||||
|
@ -96,7 +96,7 @@ static int oxnas_nand_probe(struct platform_device *pdev)
|
|||
if (!oxnas)
|
||||
return -ENOMEM;
|
||||
|
||||
nand_hw_control_init(&oxnas->base);
|
||||
nand_controller_init(&oxnas->base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
oxnas->io_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
|
|
@ -67,12 +67,10 @@ static int plat_nand_probe(struct platform_device *pdev)
|
|||
data->chip.select_chip = pdata->ctrl.select_chip;
|
||||
data->chip.write_buf = pdata->ctrl.write_buf;
|
||||
data->chip.read_buf = pdata->ctrl.read_buf;
|
||||
data->chip.read_byte = pdata->ctrl.read_byte;
|
||||
data->chip.chip_delay = pdata->chip.chip_delay;
|
||||
data->chip.options |= pdata->chip.options;
|
||||
data->chip.bbt_options |= pdata->chip.bbt_options;
|
||||
|
||||
data->chip.ecc.hwctl = pdata->ctrl.hwcontrol;
|
||||
data->chip.ecc.mode = NAND_ECC_SOFT;
|
||||
data->chip.ecc.algo = NAND_ECC_HAMMING;
|
||||
|
||||
|
|
|
@ -213,6 +213,8 @@ nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \
|
|||
#define QPIC_PER_CW_CMD_SGL 32
|
||||
#define QPIC_PER_CW_DATA_SGL 8
|
||||
|
||||
#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000)
|
||||
|
||||
/*
|
||||
* Flags used in DMA descriptor preparation helper functions
|
||||
* (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma)
|
||||
|
@ -245,6 +247,11 @@ nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \
|
|||
* @tx_sgl_start - start index in data sgl for tx.
|
||||
* @rx_sgl_pos - current index in data sgl for rx.
|
||||
* @rx_sgl_start - start index in data sgl for rx.
|
||||
* @wait_second_completion - wait for second DMA desc completion before making
|
||||
* the NAND transfer completion.
|
||||
* @txn_done - completion for NAND transfer.
|
||||
* @last_data_desc - last DMA desc in data channel (tx/rx).
|
||||
* @last_cmd_desc - last DMA desc in command channel.
|
||||
*/
|
||||
struct bam_transaction {
|
||||
struct bam_cmd_element *bam_ce;
|
||||
|
@ -258,6 +265,10 @@ struct bam_transaction {
|
|||
u32 tx_sgl_start;
|
||||
u32 rx_sgl_pos;
|
||||
u32 rx_sgl_start;
|
||||
bool wait_second_completion;
|
||||
struct completion txn_done;
|
||||
struct dma_async_tx_descriptor *last_data_desc;
|
||||
struct dma_async_tx_descriptor *last_cmd_desc;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -354,7 +365,7 @@ struct nandc_regs {
|
|||
* from all connected NAND devices pagesize
|
||||
*/
|
||||
struct qcom_nand_controller {
|
||||
struct nand_hw_control controller;
|
||||
struct nand_controller controller;
|
||||
struct list_head host_list;
|
||||
|
||||
struct device *dev;
|
||||
|
@ -504,6 +515,8 @@ alloc_bam_transaction(struct qcom_nand_controller *nandc)
|
|||
|
||||
bam_txn->data_sgl = bam_txn_buf;
|
||||
|
||||
init_completion(&bam_txn->txn_done);
|
||||
|
||||
return bam_txn;
|
||||
}
|
||||
|
||||
|
@ -523,11 +536,33 @@ static void clear_bam_transaction(struct qcom_nand_controller *nandc)
|
|||
bam_txn->tx_sgl_start = 0;
|
||||
bam_txn->rx_sgl_pos = 0;
|
||||
bam_txn->rx_sgl_start = 0;
|
||||
bam_txn->last_data_desc = NULL;
|
||||
bam_txn->wait_second_completion = false;
|
||||
|
||||
sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
|
||||
QPIC_PER_CW_CMD_SGL);
|
||||
sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage *
|
||||
QPIC_PER_CW_DATA_SGL);
|
||||
|
||||
reinit_completion(&bam_txn->txn_done);
|
||||
}
|
||||
|
||||
/* Callback for DMA descriptor completion */
|
||||
static void qpic_bam_dma_done(void *data)
|
||||
{
|
||||
struct bam_transaction *bam_txn = data;
|
||||
|
||||
/*
|
||||
* In case of data transfer with NAND, 2 callbacks will be generated.
|
||||
* One for command channel and another one for data channel.
|
||||
* If current transaction has data descriptors
|
||||
* (i.e. wait_second_completion is true), then set this to false
|
||||
* and wait for second DMA descriptor completion.
|
||||
*/
|
||||
if (bam_txn->wait_second_completion)
|
||||
bam_txn->wait_second_completion = false;
|
||||
else
|
||||
complete(&bam_txn->txn_done);
|
||||
}
|
||||
|
||||
static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
|
||||
|
@ -756,6 +791,12 @@ static int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
|
|||
|
||||
desc->dma_desc = dma_desc;
|
||||
|
||||
/* update last data/command descriptor */
|
||||
if (chan == nandc->cmd_chan)
|
||||
bam_txn->last_cmd_desc = dma_desc;
|
||||
else
|
||||
bam_txn->last_data_desc = dma_desc;
|
||||
|
||||
list_add_tail(&desc->node, &nandc->desc_list);
|
||||
|
||||
return 0;
|
||||
|
@ -1055,7 +1096,8 @@ static void config_nand_page_read(struct qcom_nand_controller *nandc)
|
|||
* Helper to prepare DMA descriptors for configuring registers
|
||||
* before reading each codeword in NAND page.
|
||||
*/
|
||||
static void config_nand_cw_read(struct qcom_nand_controller *nandc)
|
||||
static void
|
||||
config_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc)
|
||||
{
|
||||
if (nandc->props->is_bam)
|
||||
write_reg_dma(nandc, NAND_READ_LOCATION_0, 4,
|
||||
|
@ -1064,19 +1106,25 @@ static void config_nand_cw_read(struct qcom_nand_controller *nandc)
|
|||
write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
|
||||
write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
|
||||
|
||||
read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
|
||||
read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
|
||||
NAND_BAM_NEXT_SGL);
|
||||
if (use_ecc) {
|
||||
read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
|
||||
read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
|
||||
NAND_BAM_NEXT_SGL);
|
||||
} else {
|
||||
read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper to prepare dma descriptors to configure registers needed for reading a
|
||||
* single codeword in page
|
||||
*/
|
||||
static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc)
|
||||
static void
|
||||
config_nand_single_cw_page_read(struct qcom_nand_controller *nandc,
|
||||
bool use_ecc)
|
||||
{
|
||||
config_nand_page_read(nandc);
|
||||
config_nand_cw_read(nandc);
|
||||
config_nand_cw_read(nandc, use_ecc);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1157,7 +1205,7 @@ static int nandc_param(struct qcom_nand_host *host)
|
|||
nandc->buf_count = 512;
|
||||
memset(nandc->data_buffer, 0xff, nandc->buf_count);
|
||||
|
||||
config_nand_single_cw_page_read(nandc);
|
||||
config_nand_single_cw_page_read(nandc, false);
|
||||
|
||||
read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
|
||||
nandc->buf_count, 0);
|
||||
|
@ -1273,10 +1321,20 @@ static int submit_descs(struct qcom_nand_controller *nandc)
|
|||
cookie = dmaengine_submit(desc->dma_desc);
|
||||
|
||||
if (nandc->props->is_bam) {
|
||||
bam_txn->last_cmd_desc->callback = qpic_bam_dma_done;
|
||||
bam_txn->last_cmd_desc->callback_param = bam_txn;
|
||||
if (bam_txn->last_data_desc) {
|
||||
bam_txn->last_data_desc->callback = qpic_bam_dma_done;
|
||||
bam_txn->last_data_desc->callback_param = bam_txn;
|
||||
bam_txn->wait_second_completion = true;
|
||||
}
|
||||
|
||||
dma_async_issue_pending(nandc->tx_chan);
|
||||
dma_async_issue_pending(nandc->rx_chan);
|
||||
dma_async_issue_pending(nandc->cmd_chan);
|
||||
|
||||
if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE)
|
||||
if (!wait_for_completion_timeout(&bam_txn->txn_done,
|
||||
QPIC_NAND_COMPLETION_TIMEOUT))
|
||||
return -ETIMEDOUT;
|
||||
} else {
|
||||
if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
|
||||
|
@ -1512,20 +1570,180 @@ struct read_stats {
|
|||
__le32 erased_cw;
|
||||
};
|
||||
|
||||
/* reads back FLASH_STATUS register set by the controller */
|
||||
static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cw_cnt; i++) {
|
||||
u32 flash = le32_to_cpu(nandc->reg_read_buf[i]);
|
||||
|
||||
if (flash & (FS_OP_ERR | FS_MPU_ERR))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* performs raw read for one codeword */
|
||||
static int
|
||||
qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
u8 *data_buf, u8 *oob_buf, int page, int cw)
|
||||
{
|
||||
struct qcom_nand_host *host = to_qcom_nand_host(chip);
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
int data_size1, data_size2, oob_size1, oob_size2;
|
||||
int ret, reg_off = FLASH_BUF_ACC, read_loc = 0;
|
||||
|
||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
host->use_ecc = false;
|
||||
|
||||
clear_bam_transaction(nandc);
|
||||
set_address(host, host->cw_size * cw, page);
|
||||
update_rw_regs(host, 1, true);
|
||||
config_nand_page_read(nandc);
|
||||
|
||||
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
|
||||
oob_size1 = host->bbm_size;
|
||||
|
||||
if (cw == (ecc->steps - 1)) {
|
||||
data_size2 = ecc->size - data_size1 -
|
||||
((ecc->steps - 1) * 4);
|
||||
oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw +
|
||||
host->spare_bytes;
|
||||
} else {
|
||||
data_size2 = host->cw_data - data_size1;
|
||||
oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
|
||||
}
|
||||
|
||||
if (nandc->props->is_bam) {
|
||||
nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0);
|
||||
read_loc += data_size1;
|
||||
|
||||
nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0);
|
||||
read_loc += oob_size1;
|
||||
|
||||
nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0);
|
||||
read_loc += data_size2;
|
||||
|
||||
nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1);
|
||||
}
|
||||
|
||||
config_nand_cw_read(nandc, false);
|
||||
|
||||
read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
|
||||
reg_off += data_size1;
|
||||
|
||||
read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
|
||||
reg_off += oob_size1;
|
||||
|
||||
read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0);
|
||||
reg_off += data_size2;
|
||||
|
||||
read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0);
|
||||
|
||||
ret = submit_descs(nandc);
|
||||
free_descs(nandc);
|
||||
if (ret) {
|
||||
dev_err(nandc->dev, "failure to read raw cw %d\n", cw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return check_flash_errors(host, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitflips can happen in erased codewords also so this function counts the
|
||||
* number of 0 in each CW for which ECC engine returns the uncorrectable
|
||||
* error. The page will be assumed as erased if this count is less than or
|
||||
* equal to the ecc->strength for each CW.
|
||||
*
|
||||
* 1. Both DATA and OOB need to be checked for number of 0. The
|
||||
* top-level API can be called with only data buf or OOB buf so use
|
||||
* chip->data_buf if data buf is null and chip->oob_poi if oob buf
|
||||
* is null for copying the raw bytes.
|
||||
* 2. Perform raw read for all the CW which has uncorrectable errors.
|
||||
* 3. For each CW, check the number of 0 in cw_data and usable OOB bytes.
|
||||
* The BBM and spare bytes bit flip won’t affect the ECC so don’t check
|
||||
* the number of bitflips in this area.
|
||||
*/
|
||||
static int
|
||||
check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf,
|
||||
u8 *oob_buf, unsigned long uncorrectable_cws,
|
||||
int page, unsigned int max_bitflips)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
u8 *cw_data_buf, *cw_oob_buf;
|
||||
int cw, data_size, oob_size, ret = 0;
|
||||
|
||||
if (!data_buf) {
|
||||
data_buf = chip->data_buf;
|
||||
chip->pagebuf = -1;
|
||||
}
|
||||
|
||||
if (!oob_buf) {
|
||||
oob_buf = chip->oob_poi;
|
||||
chip->pagebuf = -1;
|
||||
}
|
||||
|
||||
for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) {
|
||||
if (cw == (ecc->steps - 1)) {
|
||||
data_size = ecc->size - ((ecc->steps - 1) * 4);
|
||||
oob_size = (ecc->steps * 4) + host->ecc_bytes_hw;
|
||||
} else {
|
||||
data_size = host->cw_data;
|
||||
oob_size = host->ecc_bytes_hw;
|
||||
}
|
||||
|
||||
/* determine starting buffer address for current CW */
|
||||
cw_data_buf = data_buf + (cw * host->cw_data);
|
||||
cw_oob_buf = oob_buf + (cw * ecc->bytes);
|
||||
|
||||
ret = qcom_nandc_read_cw_raw(mtd, chip, cw_data_buf,
|
||||
cw_oob_buf, page, cw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* make sure it isn't an erased page reported
|
||||
* as not-erased by HW because of a few bitflips
|
||||
*/
|
||||
ret = nand_check_erased_ecc_chunk(cw_data_buf, data_size,
|
||||
cw_oob_buf + host->bbm_size,
|
||||
oob_size, NULL,
|
||||
0, ecc->strength);
|
||||
if (ret < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += ret;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, ret);
|
||||
}
|
||||
}
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/*
|
||||
* reads back status registers set by the controller to notify page read
|
||||
* errors. this is equivalent to what 'ecc->correct()' would do.
|
||||
*/
|
||||
static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
||||
u8 *oob_buf)
|
||||
u8 *oob_buf, int page)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
unsigned int max_bitflips = 0;
|
||||
unsigned int max_bitflips = 0, uncorrectable_cws = 0;
|
||||
struct read_stats *buf;
|
||||
bool flash_op_err = false, erased;
|
||||
int i;
|
||||
u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
|
||||
|
||||
buf = (struct read_stats *)nandc->reg_read_buf;
|
||||
nandc_read_buffer_sync(nandc, true);
|
||||
|
@ -1546,48 +1764,49 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
|||
buffer = le32_to_cpu(buf->buffer);
|
||||
erased_cw = le32_to_cpu(buf->erased_cw);
|
||||
|
||||
if (flash & (FS_OP_ERR | FS_MPU_ERR)) {
|
||||
bool erased;
|
||||
|
||||
/* ignore erased codeword errors */
|
||||
/*
|
||||
* Check ECC failure for each codeword. ECC failure can
|
||||
* happen in either of the following conditions
|
||||
* 1. If number of bitflips are greater than ECC engine
|
||||
* capability.
|
||||
* 2. If this codeword contains all 0xff for which erased
|
||||
* codeword detection check will be done.
|
||||
*/
|
||||
if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) {
|
||||
/*
|
||||
* For BCH ECC, ignore erased codeword errors, if
|
||||
* ERASED_CW bits are set.
|
||||
*/
|
||||
if (host->bch_enabled) {
|
||||
erased = (erased_cw & ERASED_CW) == ERASED_CW ?
|
||||
true : false;
|
||||
} else {
|
||||
/*
|
||||
* For RS ECC, HW reports the erased CW by placing
|
||||
* special characters at certain offsets in the buffer.
|
||||
* These special characters will be valid only if
|
||||
* complete page is read i.e. data_buf is not NULL.
|
||||
*/
|
||||
} else if (data_buf) {
|
||||
erased = erased_chunk_check_and_fixup(data_buf,
|
||||
data_len);
|
||||
} else {
|
||||
erased = false;
|
||||
}
|
||||
|
||||
if (erased) {
|
||||
data_buf += data_len;
|
||||
if (oob_buf)
|
||||
oob_buf += oob_len + ecc->bytes;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buffer & BS_UNCORRECTABLE_BIT) {
|
||||
int ret, ecclen, extraooblen;
|
||||
void *eccbuf;
|
||||
|
||||
eccbuf = oob_buf ? oob_buf + oob_len : NULL;
|
||||
ecclen = oob_buf ? host->ecc_bytes_hw : 0;
|
||||
extraooblen = oob_buf ? oob_len : 0;
|
||||
|
||||
/*
|
||||
* make sure it isn't an erased page reported
|
||||
* as not-erased by HW because of a few bitflips
|
||||
*/
|
||||
ret = nand_check_erased_ecc_chunk(data_buf,
|
||||
data_len, eccbuf, ecclen, oob_buf,
|
||||
extraooblen, ecc->strength);
|
||||
if (ret < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += ret;
|
||||
max_bitflips =
|
||||
max_t(unsigned int, max_bitflips, ret);
|
||||
}
|
||||
}
|
||||
if (!erased)
|
||||
uncorrectable_cws |= BIT(i);
|
||||
/*
|
||||
* Check if MPU or any other operational error (timeout,
|
||||
* device failure, etc.) happened for this codeword and
|
||||
* make flash_op_err true. If flash_op_err is set, then
|
||||
* EIO will be returned for page read.
|
||||
*/
|
||||
} else if (flash & (FS_OP_ERR | FS_MPU_ERR)) {
|
||||
flash_op_err = true;
|
||||
/*
|
||||
* No ECC or operational errors happened. Check the number of
|
||||
* bits corrected and update the ecc_stats.corrected.
|
||||
*/
|
||||
} else {
|
||||
unsigned int stat;
|
||||
|
||||
|
@ -1596,12 +1815,21 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
|||
max_bitflips = max(max_bitflips, stat);
|
||||
}
|
||||
|
||||
data_buf += data_len;
|
||||
if (data_buf)
|
||||
data_buf += data_len;
|
||||
if (oob_buf)
|
||||
oob_buf += oob_len + ecc->bytes;
|
||||
}
|
||||
|
||||
return max_bitflips;
|
||||
if (flash_op_err)
|
||||
return -EIO;
|
||||
|
||||
if (!uncorrectable_cws)
|
||||
return max_bitflips;
|
||||
|
||||
return check_for_erased_page(host, data_buf_start, oob_buf_start,
|
||||
uncorrectable_cws, page,
|
||||
max_bitflips);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1609,11 +1837,12 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
|||
* ecc->read_oob()
|
||||
*/
|
||||
static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
|
||||
u8 *oob_buf)
|
||||
u8 *oob_buf, int page)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
|
||||
int i, ret;
|
||||
|
||||
config_nand_page_read(nandc);
|
||||
|
@ -1644,7 +1873,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
|
|||
}
|
||||
}
|
||||
|
||||
config_nand_cw_read(nandc);
|
||||
config_nand_cw_read(nandc, true);
|
||||
|
||||
if (data_buf)
|
||||
read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
|
||||
|
@ -1674,12 +1903,14 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
|
|||
}
|
||||
|
||||
ret = submit_descs(nandc);
|
||||
if (ret)
|
||||
dev_err(nandc->dev, "failure to read page/oob\n");
|
||||
|
||||
free_descs(nandc);
|
||||
|
||||
return ret;
|
||||
if (ret) {
|
||||
dev_err(nandc->dev, "failure to read page/oob\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return parse_read_errors(host, data_buf_start, oob_buf_start, page);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1704,7 +1935,7 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
|
|||
set_address(host, host->cw_size * (ecc->steps - 1), page);
|
||||
update_rw_regs(host, 1, true);
|
||||
|
||||
config_nand_single_cw_page_read(nandc);
|
||||
config_nand_single_cw_page_read(nandc, host->use_ecc);
|
||||
|
||||
read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
|
||||
|
||||
|
@ -1724,20 +1955,14 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
struct qcom_nand_host *host = to_qcom_nand_host(chip);
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
u8 *data_buf, *oob_buf = NULL;
|
||||
int ret;
|
||||
|
||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
data_buf = buf;
|
||||
oob_buf = oob_required ? chip->oob_poi : NULL;
|
||||
|
||||
clear_bam_transaction(nandc);
|
||||
ret = read_page_ecc(host, data_buf, oob_buf);
|
||||
if (ret) {
|
||||
dev_err(nandc->dev, "failure to read page\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return parse_read_errors(host, data_buf, oob_buf);
|
||||
return read_page_ecc(host, data_buf, oob_buf, page);
|
||||
}
|
||||
|
||||
/* implements ecc->read_page_raw() */
|
||||
|
@ -1746,77 +1971,20 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd,
|
|||
int oob_required, int page)
|
||||
{
|
||||
struct qcom_nand_host *host = to_qcom_nand_host(chip);
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
u8 *data_buf, *oob_buf;
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
int i, ret;
|
||||
int read_loc;
|
||||
int cw, ret;
|
||||
u8 *data_buf = buf, *oob_buf = chip->oob_poi;
|
||||
|
||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
data_buf = buf;
|
||||
oob_buf = chip->oob_poi;
|
||||
for (cw = 0; cw < ecc->steps; cw++) {
|
||||
ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf,
|
||||
page, cw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host->use_ecc = false;
|
||||
|
||||
clear_bam_transaction(nandc);
|
||||
update_rw_regs(host, ecc->steps, true);
|
||||
config_nand_page_read(nandc);
|
||||
|
||||
for (i = 0; i < ecc->steps; i++) {
|
||||
int data_size1, data_size2, oob_size1, oob_size2;
|
||||
int reg_off = FLASH_BUF_ACC;
|
||||
|
||||
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
|
||||
oob_size1 = host->bbm_size;
|
||||
|
||||
if (i == (ecc->steps - 1)) {
|
||||
data_size2 = ecc->size - data_size1 -
|
||||
((ecc->steps - 1) << 2);
|
||||
oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
|
||||
host->spare_bytes;
|
||||
} else {
|
||||
data_size2 = host->cw_data - data_size1;
|
||||
oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
|
||||
}
|
||||
|
||||
if (nandc->props->is_bam) {
|
||||
read_loc = 0;
|
||||
nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0);
|
||||
read_loc += data_size1;
|
||||
|
||||
nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0);
|
||||
read_loc += oob_size1;
|
||||
|
||||
nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0);
|
||||
read_loc += data_size2;
|
||||
|
||||
nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1);
|
||||
}
|
||||
|
||||
config_nand_cw_read(nandc);
|
||||
|
||||
read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
|
||||
reg_off += data_size1;
|
||||
data_buf += data_size1;
|
||||
|
||||
read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
|
||||
reg_off += oob_size1;
|
||||
oob_buf += oob_size1;
|
||||
|
||||
read_data_dma(nandc, reg_off, data_buf, data_size2, 0);
|
||||
reg_off += data_size2;
|
||||
data_buf += data_size2;
|
||||
|
||||
read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
|
||||
oob_buf += oob_size2;
|
||||
data_buf += host->cw_data;
|
||||
oob_buf += ecc->bytes;
|
||||
}
|
||||
|
||||
ret = submit_descs(nandc);
|
||||
if (ret)
|
||||
dev_err(nandc->dev, "failure to read raw page\n");
|
||||
|
||||
free_descs(nandc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1827,7 +1995,6 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
struct qcom_nand_host *host = to_qcom_nand_host(chip);
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
int ret;
|
||||
|
||||
clear_read_regs(nandc);
|
||||
clear_bam_transaction(nandc);
|
||||
|
@ -1836,11 +2003,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
set_address(host, 0, page);
|
||||
update_rw_regs(host, ecc->steps, true);
|
||||
|
||||
ret = read_page_ecc(host, NULL, chip->oob_poi);
|
||||
if (ret)
|
||||
dev_err(nandc->dev, "failure to read oob\n");
|
||||
|
||||
return ret;
|
||||
return read_page_ecc(host, NULL, chip->oob_poi, page);
|
||||
}
|
||||
|
||||
/* implements ecc->write_page() */
|
||||
|
@ -1988,11 +2151,9 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd,
|
|||
/*
|
||||
* implements ecc->write_oob()
|
||||
*
|
||||
* the NAND controller cannot write only data or only oob within a codeword,
|
||||
* since ecc is calculated for the combined codeword. we first copy the
|
||||
* entire contents for the last codeword(data + oob), replace the old oob
|
||||
* with the new one in chip->oob_poi, and then write the entire codeword.
|
||||
* this read-copy-write operation results in a slight performance loss.
|
||||
* the NAND controller cannot write only data or only OOB within a codeword
|
||||
* since ECC is calculated for the combined codeword. So update the OOB from
|
||||
* chip->oob_poi, and pad the data area with OxFF before writing.
|
||||
*/
|
||||
static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page)
|
||||
|
@ -2005,19 +2166,13 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int ret;
|
||||
|
||||
host->use_ecc = true;
|
||||
|
||||
clear_bam_transaction(nandc);
|
||||
ret = copy_last_cw(host, page);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clear_read_regs(nandc);
|
||||
clear_bam_transaction(nandc);
|
||||
|
||||
/* calculate the data and oob size for the last codeword/step */
|
||||
data_size = ecc->size - ((ecc->steps - 1) << 2);
|
||||
oob_size = mtd->oobavail;
|
||||
|
||||
memset(nandc->data_buffer, 0xff, host->cw_data);
|
||||
/* override new oob content to last codeword */
|
||||
mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob,
|
||||
0, mtd->oobavail);
|
||||
|
@ -2049,7 +2204,6 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs)
|
|||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
int page, ret, bbpos, bad = 0;
|
||||
u32 flash_status;
|
||||
|
||||
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
|
||||
|
||||
|
@ -2066,9 +2220,7 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs)
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
flash_status = le32_to_cpu(nandc->reg_read_buf[0]);
|
||||
|
||||
if (flash_status & (FS_OP_ERR | FS_MPU_ERR)) {
|
||||
if (check_flash_errors(host, 1)) {
|
||||
dev_warn(nandc->dev, "error when trying to read BBM\n");
|
||||
goto err;
|
||||
}
|
||||
|
@ -2315,26 +2467,39 @@ static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = {
|
|||
.free = qcom_nand_ooblayout_free,
|
||||
};
|
||||
|
||||
static int qcom_nand_host_setup(struct qcom_nand_host *host)
|
||||
static int
|
||||
qcom_nandc_calc_ecc_bytes(int step_size, int strength)
|
||||
{
|
||||
return strength == 4 ? 12 : 16;
|
||||
}
|
||||
NAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes,
|
||||
NANDC_STEP_SIZE, 4, 8);
|
||||
|
||||
static int qcom_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct qcom_nand_host *host = to_qcom_nand_host(chip);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
int cwperpage, bad_block_byte;
|
||||
int cwperpage, bad_block_byte, ret;
|
||||
bool wide_bus;
|
||||
int ecc_mode = 1;
|
||||
|
||||
/*
|
||||
* the controller requires each step consists of 512 bytes of data.
|
||||
* bail out if DT has populated a wrong step size.
|
||||
*/
|
||||
if (ecc->size != NANDC_STEP_SIZE) {
|
||||
dev_err(nandc->dev, "invalid ecc size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* controller only supports 512 bytes data steps */
|
||||
ecc->size = NANDC_STEP_SIZE;
|
||||
wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
|
||||
cwperpage = mtd->writesize / NANDC_STEP_SIZE;
|
||||
|
||||
/*
|
||||
* Each CW has 4 available OOB bytes which will be protected with ECC
|
||||
* so remaining bytes can be used for ECC.
|
||||
*/
|
||||
ret = nand_ecc_choose_conf(chip, &qcom_nandc_ecc_caps,
|
||||
mtd->oobsize - (cwperpage * 4));
|
||||
if (ret) {
|
||||
dev_err(nandc->dev, "No valid ECC settings possible\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ecc->strength >= 8) {
|
||||
/* 8 bit ECC defaults to BCH ECC on all platforms */
|
||||
|
@ -2403,7 +2568,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
|
|||
|
||||
mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
|
||||
|
||||
cwperpage = mtd->writesize / ecc->size;
|
||||
nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
|
||||
cwperpage);
|
||||
|
||||
|
@ -2419,12 +2583,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
|
|||
* for 8 bit ECC
|
||||
*/
|
||||
host->cw_size = host->cw_data + ecc->bytes;
|
||||
|
||||
if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) {
|
||||
dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1;
|
||||
|
||||
host->cfg0 = (cwperpage - 1) << CW_PER_PAGE
|
||||
|
@ -2482,6 +2640,10 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops qcom_nandc_ops = {
|
||||
.attach_chip = qcom_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
|
||||
{
|
||||
int ret;
|
||||
|
@ -2570,7 +2732,8 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
|
|||
INIT_LIST_HEAD(&nandc->desc_list);
|
||||
INIT_LIST_HEAD(&nandc->host_list);
|
||||
|
||||
nand_hw_control_init(&nandc->controller);
|
||||
nand_controller_init(&nandc->controller);
|
||||
nandc->controller.ops = &qcom_nandc_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2623,9 +2786,9 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_nand_host_init(struct qcom_nand_controller *nandc,
|
||||
struct qcom_nand_host *host,
|
||||
struct device_node *dn)
|
||||
static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
|
||||
struct qcom_nand_host *host,
|
||||
struct device_node *dn)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
@ -2672,30 +2835,13 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc,
|
|||
/* set up initial status value */
|
||||
host->status = NAND_STATUS_READY | NAND_STATUS_WP;
|
||||
|
||||
ret = nand_scan_ident(mtd, 1, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_nand_host_setup(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_nand_mtd_register(struct qcom_nand_controller *nandc,
|
||||
struct qcom_nand_host *host,
|
||||
struct device_node *dn)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
ret = nand_scan(mtd, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret)
|
||||
nand_cleanup(mtd_to_nand(mtd));
|
||||
nand_cleanup(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -2704,28 +2850,9 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
|
|||
{
|
||||
struct device *dev = nandc->dev;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
struct qcom_nand_host *host, *tmp;
|
||||
struct qcom_nand_host *host;
|
||||
int ret;
|
||||
|
||||
for_each_available_child_of_node(dn, child) {
|
||||
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
|
||||
if (!host) {
|
||||
of_node_put(child);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = qcom_nand_host_init(nandc, host, child);
|
||||
if (ret) {
|
||||
devm_kfree(dev, host);
|
||||
continue;
|
||||
}
|
||||
|
||||
list_add_tail(&host->node, &nandc->host_list);
|
||||
}
|
||||
|
||||
if (list_empty(&nandc->host_list))
|
||||
return -ENODEV;
|
||||
|
||||
if (nandc->props->is_bam) {
|
||||
free_bam_transaction(nandc);
|
||||
nandc->bam_txn = alloc_bam_transaction(nandc);
|
||||
|
@ -2736,12 +2863,20 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
|
|||
}
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(host, tmp, &nandc->host_list, node) {
|
||||
ret = qcom_nand_mtd_register(nandc, host, child);
|
||||
if (ret) {
|
||||
list_del(&host->node);
|
||||
devm_kfree(dev, host);
|
||||
for_each_available_child_of_node(dn, child) {
|
||||
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
|
||||
if (!host) {
|
||||
of_node_put(child);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = qcom_nand_host_init_and_register(nandc, host, child);
|
||||
if (ret) {
|
||||
devm_kfree(dev, host);
|
||||
continue;
|
||||
}
|
||||
|
||||
list_add_tail(&host->node, &nandc->host_list);
|
||||
}
|
||||
|
||||
if (list_empty(&nandc->host_list))
|
||||
|
@ -2799,14 +2934,6 @@ static int qcom_nandc_probe(struct platform_device *pdev)
|
|||
|
||||
nandc->props = dev_data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
nandc->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(nandc->base))
|
||||
return PTR_ERR(nandc->base);
|
||||
|
||||
nandc->base_phys = res->start;
|
||||
nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start);
|
||||
|
||||
nandc->core_clk = devm_clk_get(dev, "core");
|
||||
if (IS_ERR(nandc->core_clk))
|
||||
return PTR_ERR(nandc->core_clk);
|
||||
|
@ -2819,9 +2946,21 @@ static int qcom_nandc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
nandc->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(nandc->base))
|
||||
return PTR_ERR(nandc->base);
|
||||
|
||||
nandc->base_phys = res->start;
|
||||
nandc->base_dma = dma_map_resource(dev, res->start,
|
||||
resource_size(res),
|
||||
DMA_BIDIRECTIONAL, 0);
|
||||
if (!nandc->base_dma)
|
||||
return -ENXIO;
|
||||
|
||||
ret = qcom_nandc_alloc(nandc);
|
||||
if (ret)
|
||||
goto err_core_clk;
|
||||
goto err_nandc_alloc;
|
||||
|
||||
ret = clk_prepare_enable(nandc->core_clk);
|
||||
if (ret)
|
||||
|
@ -2847,6 +2986,9 @@ err_aon_clk:
|
|||
clk_disable_unprepare(nandc->core_clk);
|
||||
err_core_clk:
|
||||
qcom_nandc_unalloc(nandc);
|
||||
err_nandc_alloc:
|
||||
dma_unmap_resource(dev, res->start, resource_size(res),
|
||||
DMA_BIDIRECTIONAL, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -2854,16 +2996,21 @@ err_core_clk:
|
|||
static int qcom_nandc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_nand_controller *nandc = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
struct qcom_nand_host *host;
|
||||
|
||||
list_for_each_entry(host, &nandc->host_list, node)
|
||||
nand_release(nand_to_mtd(&host->chip));
|
||||
|
||||
|
||||
qcom_nandc_unalloc(nandc);
|
||||
|
||||
clk_disable_unprepare(nandc->aon_clk);
|
||||
clk_disable_unprepare(nandc->core_clk);
|
||||
|
||||
dma_unmap_resource(&pdev->dev, nandc->base_dma, resource_size(res),
|
||||
DMA_BIDIRECTIONAL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ enum s3c_nand_clk_state {
|
|||
*/
|
||||
struct s3c2410_nand_info {
|
||||
/* mtd info */
|
||||
struct nand_hw_control controller;
|
||||
struct nand_controller controller;
|
||||
struct s3c2410_nand_mtd *mtds;
|
||||
struct s3c2410_platform_nand *platform;
|
||||
|
||||
|
@ -802,8 +802,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
|||
|
||||
mtdinfo->name = set->name;
|
||||
|
||||
return mtd_device_parse_register(mtdinfo, NULL, NULL,
|
||||
set->partitions, set->nr_partitions);
|
||||
return mtd_device_register(mtdinfo, set->partitions,
|
||||
set->nr_partitions);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
|
@ -915,20 +915,19 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
|||
}
|
||||
|
||||
/**
|
||||
* s3c2410_nand_update_chip - post probe update
|
||||
* @info: The controller instance.
|
||||
* @nmtd: The driver version of the MTD instance.
|
||||
* s3c2410_nand_attach_chip - Init the ECC engine after NAND scan
|
||||
* @chip: The NAND chip
|
||||
*
|
||||
* This routine is called after the chip probe has successfully completed
|
||||
* and the relevant per-chip information updated. This call ensure that
|
||||
* This hook is called by the core after the identification of the NAND chip,
|
||||
* once the relevant per-chip information is up to date.. This call ensure that
|
||||
* we update the internal state accordingly.
|
||||
*
|
||||
* The internal state is currently limited to the ECC state information.
|
||||
*/
|
||||
static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
|
||||
struct s3c2410_nand_mtd *nmtd)
|
||||
static int s3c2410_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = &nmtd->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
|
||||
switch (chip->ecc.mode) {
|
||||
|
||||
|
@ -998,6 +997,10 @@ static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops s3c24xx_nand_controller_ops = {
|
||||
.attach_chip = s3c2410_nand_attach_chip,
|
||||
};
|
||||
|
||||
static const struct of_device_id s3c24xx_nand_dt_ids[] = {
|
||||
{
|
||||
.compatible = "samsung,s3c2410-nand",
|
||||
|
@ -1094,7 +1097,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
nand_hw_control_init(&info->controller);
|
||||
nand_controller_init(&info->controller);
|
||||
info->controller.ops = &s3c24xx_nand_controller_ops;
|
||||
|
||||
/* get the clock source and enable it */
|
||||
|
||||
|
@ -1134,8 +1138,13 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
|||
|
||||
dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
|
||||
|
||||
sets = (plat != NULL) ? plat->sets : NULL;
|
||||
nr_sets = (plat != NULL) ? plat->nr_sets : 1;
|
||||
if (!plat->sets || plat->nr_sets < 1) {
|
||||
err = -EINVAL;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
sets = plat->sets;
|
||||
nr_sets = plat->nr_sets;
|
||||
|
||||
info->mtd_count = nr_sets;
|
||||
|
||||
|
@ -1152,7 +1161,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
|||
|
||||
nmtd = info->mtds;
|
||||
|
||||
for (setno = 0; setno < nr_sets; setno++, nmtd++) {
|
||||
for (setno = 0; setno < nr_sets; setno++, nmtd++, sets++) {
|
||||
struct mtd_info *mtd = nand_to_mtd(&nmtd->chip);
|
||||
|
||||
pr_debug("initialising set %d (%p, info %p)\n",
|
||||
|
@ -1161,22 +1170,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
|
|||
mtd->dev.parent = &pdev->dev;
|
||||
s3c2410_nand_init_chip(info, nmtd, sets);
|
||||
|
||||
err = nand_scan_ident(mtd, (sets) ? sets->nr_chips : 1, NULL);
|
||||
if (err)
|
||||
goto exit_error;
|
||||
|
||||
err = s3c2410_nand_update_chip(info, nmtd);
|
||||
if (err < 0)
|
||||
goto exit_error;
|
||||
|
||||
err = nand_scan_tail(mtd);
|
||||
err = nand_scan(mtd, sets ? sets->nr_chips : 1);
|
||||
if (err)
|
||||
goto exit_error;
|
||||
|
||||
s3c2410_nand_add_partition(info, nmtd, sets);
|
||||
|
||||
if (sets != NULL)
|
||||
sets++;
|
||||
}
|
||||
|
||||
/* initialise the hardware */
|
||||
|
|
|
@ -1002,10 +1002,17 @@ static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|||
flctl->index += len;
|
||||
}
|
||||
|
||||
static int flctl_chip_init_tail(struct mtd_info *mtd)
|
||||
static int flctl_chip_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct sh_flctl *flctl = mtd_to_flctl(mtd);
|
||||
struct nand_chip *chip = &flctl->chip;
|
||||
|
||||
/*
|
||||
* NAND_BUSWIDTH_16 may have been set by nand_scan_ident().
|
||||
* Add the SEL_16BIT flag in flctl->flcmncr_base.
|
||||
*/
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
flctl->flcmncr_base |= SEL_16BIT;
|
||||
|
||||
if (mtd->writesize == 512) {
|
||||
flctl->page_size = 0;
|
||||
|
@ -1063,6 +1070,10 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops flctl_nand_controller_ops = {
|
||||
.attach_chip = flctl_chip_attach_chip,
|
||||
};
|
||||
|
||||
static irqreturn_t flctl_handle_flste(int irq, void *dev_id)
|
||||
{
|
||||
struct sh_flctl *flctl = dev_id;
|
||||
|
@ -1191,25 +1202,8 @@ static int flctl_probe(struct platform_device *pdev)
|
|||
|
||||
flctl_setup_dma(flctl);
|
||||
|
||||
ret = nand_scan_ident(flctl_mtd, 1, NULL);
|
||||
if (ret)
|
||||
goto err_chip;
|
||||
|
||||
if (nand->options & NAND_BUSWIDTH_16) {
|
||||
/*
|
||||
* NAND_BUSWIDTH_16 may have been set by nand_scan_ident().
|
||||
* Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign
|
||||
* flctl->flcmncr_base to pdata->flcmncr_val.
|
||||
*/
|
||||
pdata->flcmncr_val |= SEL_16BIT;
|
||||
flctl->flcmncr_base = pdata->flcmncr_val;
|
||||
}
|
||||
|
||||
ret = flctl_chip_init_tail(flctl_mtd);
|
||||
if (ret)
|
||||
goto err_chip;
|
||||
|
||||
ret = nand_scan_tail(flctl_mtd);
|
||||
nand->dummy_controller.ops = &flctl_nand_controller_ops;
|
||||
ret = nand_scan(flctl_mtd, 1);
|
||||
if (ret)
|
||||
goto err_chip;
|
||||
|
||||
|
|
|
@ -21,10 +21,7 @@
|
|||
#include <linux/mtd/sharpsl.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
struct sharpsl_nand {
|
||||
struct nand_chip chip;
|
||||
|
|
|
@ -160,19 +160,9 @@ static struct nand_flash_dev nand_xd_flash_ids[] = {
|
|||
{NULL}
|
||||
};
|
||||
|
||||
int sm_register_device(struct mtd_info *mtd, int smartmedia)
|
||||
static int sm_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
int ret;
|
||||
|
||||
chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
/* Scan for card properties */
|
||||
ret = nand_scan_ident(mtd, 1, smartmedia ?
|
||||
nand_smartmedia_flash_ids : nand_xd_flash_ids);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
/* Bad block marker position */
|
||||
chip->badblockpos = 0x05;
|
||||
|
@ -187,12 +177,33 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia)
|
|||
else
|
||||
return -ENODEV;
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops sm_controller_ops = {
|
||||
.attach_chip = sm_attach_chip,
|
||||
};
|
||||
|
||||
int sm_register_device(struct mtd_info *mtd, int smartmedia)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_flash_dev *flash_ids;
|
||||
int ret;
|
||||
|
||||
chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
/* Scan for card properties */
|
||||
chip->dummy_controller.ops = &sm_controller_ops;
|
||||
flash_ids = smartmedia ? nand_smartmedia_flash_ids : nand_xd_flash_ids;
|
||||
ret = nand_scan_with_ids(mtd, 1, flash_ids);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mtd_device_register(mtd, NULL, 0);
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret)
|
||||
nand_cleanup(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sm_register_device);
|
||||
|
||||
|
|
|
@ -29,14 +29,12 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/reset.h>
|
||||
|
@ -127,7 +125,7 @@
|
|||
#define NFC_CMD_TYPE_MSK GENMASK(31, 30)
|
||||
#define NFC_NORMAL_OP (0 << 30)
|
||||
#define NFC_ECC_OP (1 << 30)
|
||||
#define NFC_PAGE_OP (2 << 30)
|
||||
#define NFC_PAGE_OP (2U << 30)
|
||||
|
||||
/* define bit use in NFC_RCMD_SET */
|
||||
#define NFC_READ_CMD_MSK GENMASK(7, 0)
|
||||
|
@ -234,7 +232,7 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
|
|||
* controller events
|
||||
*/
|
||||
struct sunxi_nfc {
|
||||
struct nand_hw_control controller;
|
||||
struct nand_controller controller;
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *ahb_clk;
|
||||
|
@ -247,7 +245,7 @@ struct sunxi_nfc {
|
|||
struct dma_chan *dmac;
|
||||
};
|
||||
|
||||
static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
|
||||
static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl)
|
||||
{
|
||||
return container_of(ctrl, struct sunxi_nfc, controller);
|
||||
}
|
||||
|
@ -544,7 +542,7 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
|
|||
|
||||
static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
uint8_t ret;
|
||||
uint8_t ret = 0;
|
||||
|
||||
sunxi_nfc_read_buf(mtd, &ret, 1);
|
||||
|
||||
|
@ -1816,12 +1814,21 @@ static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
|
|||
}
|
||||
}
|
||||
|
||||
static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
|
||||
struct device_node *np)
|
||||
static int sunxi_nand_attach_chip(struct nand_chip *nand)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||
struct device_node *np = nand_get_flash_node(nand);
|
||||
int ret;
|
||||
|
||||
if (nand->bbt_options & NAND_BBT_USE_FLASH)
|
||||
nand->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
if (nand->options & NAND_NEED_SCRAMBLING)
|
||||
nand->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
nand->options |= NAND_SUBPAGE_READ;
|
||||
|
||||
if (!ecc->size) {
|
||||
ecc->size = nand->ecc_step_ds;
|
||||
ecc->strength = nand->ecc_strength_ds;
|
||||
|
@ -1846,6 +1853,10 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops sunxi_nand_controller_ops = {
|
||||
.attach_chip = sunxi_nand_attach_chip,
|
||||
};
|
||||
|
||||
static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
|
||||
struct device_node *np)
|
||||
{
|
||||
|
@ -1911,6 +1922,8 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
|
|||
/* Default tR value specified in the ONFI spec (chapter 4.15.1) */
|
||||
nand->chip_delay = 200;
|
||||
nand->controller = &nfc->controller;
|
||||
nand->controller->ops = &sunxi_nand_controller_ops;
|
||||
|
||||
/*
|
||||
* Set the ECC mode to the default value in case nothing is specified
|
||||
* in the DT.
|
||||
|
@ -1927,30 +1940,10 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
|
|||
mtd = nand_to_mtd(nand);
|
||||
mtd->dev.parent = dev;
|
||||
|
||||
ret = nand_scan_ident(mtd, nsels, NULL);
|
||||
ret = nand_scan(mtd, nsels);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nand->bbt_options & NAND_BBT_USE_FLASH)
|
||||
nand->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
if (nand->options & NAND_NEED_SCRAMBLING)
|
||||
nand->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
nand->options |= NAND_SUBPAGE_READ;
|
||||
|
||||
ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
|
||||
if (ret) {
|
||||
dev_err(dev, "ECC init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret) {
|
||||
dev_err(dev, "nand_scan_tail failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register mtd device: %d\n", ret);
|
||||
|
@ -2012,7 +2005,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
nfc->dev = dev;
|
||||
nand_hw_control_init(&nfc->controller);
|
||||
nand_controller_init(&nfc->controller);
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
#define MAX_CS 4
|
||||
|
||||
struct tango_nfc {
|
||||
struct nand_hw_control hw;
|
||||
struct nand_controller hw;
|
||||
void __iomem *reg_base;
|
||||
void __iomem *mem_base;
|
||||
void __iomem *pbus_base;
|
||||
|
@ -517,6 +517,28 @@ static int tango_set_timings(struct mtd_info *mtd, int csline,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tango_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
||||
ecc->mode = NAND_ECC_HW;
|
||||
ecc->algo = NAND_ECC_BCH;
|
||||
ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE);
|
||||
|
||||
ecc->read_page_raw = tango_read_page_raw;
|
||||
ecc->write_page_raw = tango_write_page_raw;
|
||||
ecc->read_page = tango_read_page;
|
||||
ecc->write_page = tango_write_page;
|
||||
ecc->read_oob = tango_read_oob;
|
||||
ecc->write_oob = tango_write_oob;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops tango_controller_ops = {
|
||||
.attach_chip = tango_attach_chip,
|
||||
};
|
||||
|
||||
static int chip_init(struct device *dev, struct device_node *np)
|
||||
{
|
||||
u32 cs;
|
||||
|
@ -566,22 +588,7 @@ static int chip_init(struct device *dev, struct device_node *np)
|
|||
mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops);
|
||||
mtd->dev.parent = dev;
|
||||
|
||||
err = nand_scan_ident(mtd, 1, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ecc->mode = NAND_ECC_HW;
|
||||
ecc->algo = NAND_ECC_BCH;
|
||||
ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE);
|
||||
|
||||
ecc->read_page_raw = tango_read_page_raw;
|
||||
ecc->write_page_raw = tango_write_page_raw;
|
||||
ecc->read_page = tango_read_page;
|
||||
ecc->write_page = tango_write_page;
|
||||
ecc->read_oob = tango_read_oob;
|
||||
ecc->write_oob = tango_write_oob;
|
||||
|
||||
err = nand_scan_tail(mtd);
|
||||
err = nand_scan(mtd, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -654,7 +661,8 @@ static int tango_nand_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(nfc->chan);
|
||||
|
||||
platform_set_drvdata(pdev, nfc);
|
||||
nand_hw_control_init(&nfc->hw);
|
||||
nand_controller_init(&nfc->hw);
|
||||
nfc->hw.ops = &tango_controller_ops;
|
||||
nfc->freq_kHz = clk_get_rate(clk) / 1000;
|
||||
|
||||
for_each_child_of_node(pdev->dev.of_node, np) {
|
||||
|
|
1246
drivers/mtd/nand/raw/tegra_nand.c
Normal file
1246
drivers/mtd/nand/raw/tegra_nand.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -20,7 +20,7 @@
|
|||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/txx9/ndfmc.h>
|
||||
#include <linux/platform_data/txx9/ndfmc.h>
|
||||
|
||||
/* TXX9 NDFMC Registers */
|
||||
#define TXX9_NDFDTR 0x00
|
||||
|
@ -73,7 +73,7 @@ struct txx9ndfmc_drvdata {
|
|||
void __iomem *base;
|
||||
unsigned char hold; /* in gbusclock */
|
||||
unsigned char spw; /* in gbusclock */
|
||||
struct nand_hw_control hw_control;
|
||||
struct nand_controller controller;
|
||||
};
|
||||
|
||||
static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)
|
||||
|
@ -254,23 +254,25 @@ static void txx9ndfmc_initialize(struct platform_device *dev)
|
|||
#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \
|
||||
DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000)
|
||||
|
||||
static int txx9ndfmc_nand_scan(struct mtd_info *mtd)
|
||||
static int txx9ndfmc_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
int ret;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
ret = nand_scan_ident(mtd, 1, NULL);
|
||||
if (!ret) {
|
||||
if (mtd->writesize >= 512) {
|
||||
/* Hardware ECC 6 byte ECC per 512 Byte data */
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 6;
|
||||
}
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (mtd->writesize >= 512) {
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 6;
|
||||
} else {
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = 3;
|
||||
}
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops txx9ndfmc_controller_ops = {
|
||||
.attach_chip = txx9ndfmc_attach_chip,
|
||||
};
|
||||
|
||||
static int __init txx9ndfmc_probe(struct platform_device *dev)
|
||||
{
|
||||
struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
|
||||
|
@ -303,7 +305,8 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
|
|||
dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n",
|
||||
(gbusclk + 500000) / 1000000, hold, spw);
|
||||
|
||||
nand_hw_control_init(&drvdata->hw_control);
|
||||
nand_controller_init(&drvdata->controller);
|
||||
drvdata->controller.ops = &txx9ndfmc_controller_ops;
|
||||
|
||||
platform_set_drvdata(dev, drvdata);
|
||||
txx9ndfmc_initialize(dev);
|
||||
|
@ -332,12 +335,9 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
|
|||
chip->ecc.correct = txx9ndfmc_correct_data;
|
||||
chip->ecc.hwctl = txx9ndfmc_enable_hwecc;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
/* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
chip->chip_delay = 100;
|
||||
chip->controller = &drvdata->hw_control;
|
||||
chip->controller = &drvdata->controller;
|
||||
|
||||
nand_set_controller_data(chip, txx9_priv);
|
||||
txx9_priv->dev = dev;
|
||||
|
@ -359,14 +359,14 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
|
|||
if (plat->wide_mask & (1 << i))
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (txx9ndfmc_nand_scan(mtd)) {
|
||||
if (nand_scan(mtd, 1)) {
|
||||
kfree(txx9_priv->mtdname);
|
||||
kfree(txx9_priv);
|
||||
continue;
|
||||
}
|
||||
mtd->name = txx9_priv->mtdname;
|
||||
|
||||
mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
|
||||
mtd_device_register(mtd, NULL, 0);
|
||||
drvdata->mtds[i] = mtd;
|
||||
}
|
||||
|
||||
|
|
|
@ -747,6 +747,69 @@ static void vf610_nfc_init_controller(struct vf610_nfc *nfc)
|
|||
}
|
||||
}
|
||||
|
||||
static int vf610_nfc_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
|
||||
vf610_nfc_init_controller(nfc);
|
||||
|
||||
/* Bad block options. */
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
/* Single buffer only, max 256 OOB minus ECC status */
|
||||
if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) {
|
||||
dev_err(nfc->dev, "Unsupported flash page size\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (chip->ecc.mode != NAND_ECC_HW)
|
||||
return 0;
|
||||
|
||||
if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) {
|
||||
dev_err(nfc->dev, "Unsupported flash with hwecc\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (chip->ecc.size != mtd->writesize) {
|
||||
dev_err(nfc->dev, "Step size needs to be page size\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Only 64 byte ECC layouts known */
|
||||
if (mtd->oobsize > 64)
|
||||
mtd->oobsize = 64;
|
||||
|
||||
/* Use default large page ECC layout defined in NAND core */
|
||||
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
|
||||
if (chip->ecc.strength == 32) {
|
||||
nfc->ecc_mode = ECC_60_BYTE;
|
||||
chip->ecc.bytes = 60;
|
||||
} else if (chip->ecc.strength == 24) {
|
||||
nfc->ecc_mode = ECC_45_BYTE;
|
||||
chip->ecc.bytes = 45;
|
||||
} else {
|
||||
dev_err(nfc->dev, "Unsupported ECC strength\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
chip->ecc.read_page = vf610_nfc_read_page;
|
||||
chip->ecc.write_page = vf610_nfc_write_page;
|
||||
chip->ecc.read_page_raw = vf610_nfc_read_page_raw;
|
||||
chip->ecc.write_page_raw = vf610_nfc_write_page_raw;
|
||||
chip->ecc.read_oob = vf610_nfc_read_oob;
|
||||
chip->ecc.write_oob = vf610_nfc_write_oob;
|
||||
|
||||
chip->ecc.size = PAGE_2K;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops vf610_nfc_controller_ops = {
|
||||
.attach_chip = vf610_nfc_attach_chip,
|
||||
};
|
||||
|
||||
static int vf610_nfc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vf610_nfc *nfc;
|
||||
|
@ -827,67 +890,9 @@ static int vf610_nfc_probe(struct platform_device *pdev)
|
|||
|
||||
vf610_nfc_preinit_controller(nfc);
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
err = nand_scan_ident(mtd, 1, NULL);
|
||||
if (err)
|
||||
goto err_disable_clk;
|
||||
|
||||
vf610_nfc_init_controller(nfc);
|
||||
|
||||
/* Bad block options. */
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
/* Single buffer only, max 256 OOB minus ECC status */
|
||||
if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) {
|
||||
dev_err(nfc->dev, "Unsupported flash page size\n");
|
||||
err = -ENXIO;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_HW) {
|
||||
if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) {
|
||||
dev_err(nfc->dev, "Unsupported flash with hwecc\n");
|
||||
err = -ENXIO;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
if (chip->ecc.size != mtd->writesize) {
|
||||
dev_err(nfc->dev, "Step size needs to be page size\n");
|
||||
err = -ENXIO;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
/* Only 64 byte ECC layouts known */
|
||||
if (mtd->oobsize > 64)
|
||||
mtd->oobsize = 64;
|
||||
|
||||
/* Use default large page ECC layout defined in NAND core */
|
||||
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
|
||||
if (chip->ecc.strength == 32) {
|
||||
nfc->ecc_mode = ECC_60_BYTE;
|
||||
chip->ecc.bytes = 60;
|
||||
} else if (chip->ecc.strength == 24) {
|
||||
nfc->ecc_mode = ECC_45_BYTE;
|
||||
chip->ecc.bytes = 45;
|
||||
} else {
|
||||
dev_err(nfc->dev, "Unsupported ECC strength\n");
|
||||
err = -ENXIO;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
chip->ecc.read_page = vf610_nfc_read_page;
|
||||
chip->ecc.write_page = vf610_nfc_write_page;
|
||||
chip->ecc.read_page_raw = vf610_nfc_read_page_raw;
|
||||
chip->ecc.write_page_raw = vf610_nfc_write_page_raw;
|
||||
chip->ecc.read_oob = vf610_nfc_read_oob;
|
||||
chip->ecc.write_oob = vf610_nfc_write_oob;
|
||||
|
||||
chip->ecc.size = PAGE_2K;
|
||||
}
|
||||
|
||||
/* second phase scan */
|
||||
err = nand_scan_tail(mtd);
|
||||
/* Scan the NAND chip */
|
||||
chip->dummy_controller.ops = &vf610_nfc_controller_ops;
|
||||
err = nand_scan(mtd, 1);
|
||||
if (err)
|
||||
goto err_disable_clk;
|
||||
|
||||
|
|
7
drivers/mtd/nand/spi/Kconfig
Normal file
7
drivers/mtd/nand/spi/Kconfig
Normal file
|
@ -0,0 +1,7 @@
|
|||
menuconfig MTD_SPI_NAND
|
||||
tristate "SPI NAND device Support"
|
||||
select MTD_NAND_CORE
|
||||
depends on SPI_MASTER
|
||||
select SPI_MEM
|
||||
help
|
||||
This is the framework for the SPI NAND device drivers.
|
3
drivers/mtd/nand/spi/Makefile
Normal file
3
drivers/mtd/nand/spi/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
spinand-objs := core.o macronix.o micron.o winbond.o
|
||||
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
|
1155
drivers/mtd/nand/spi/core.c
Normal file
1155
drivers/mtd/nand/spi/core.c
Normal file
File diff suppressed because it is too large
Load diff
144
drivers/mtd/nand/spi/macronix.c
Normal file
144
drivers/mtd/nand/spi/macronix.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018 Macronix
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/spinand.h>
|
||||
|
||||
#define SPINAND_MFR_MACRONIX 0xC2
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(write_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(true, 0, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(update_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
|
||||
static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
static int mx35lfxge4ab_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = 2;
|
||||
region->length = mtd->oobsize - 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = {
|
||||
.ecc = mx35lfxge4ab_ooblayout_ecc,
|
||||
.free = mx35lfxge4ab_ooblayout_free,
|
||||
};
|
||||
|
||||
static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
|
||||
{
|
||||
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
|
||||
SPI_MEM_OP_NO_ADDR,
|
||||
SPI_MEM_OP_DUMMY(1, 1),
|
||||
SPI_MEM_OP_DATA_IN(1, eccsr, 1));
|
||||
|
||||
return spi_mem_exec_op(spinand->spimem, &op);
|
||||
}
|
||||
|
||||
static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
|
||||
u8 status)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
u8 eccsr;
|
||||
|
||||
switch (status & STATUS_ECC_MASK) {
|
||||
case STATUS_ECC_NO_BITFLIPS:
|
||||
return 0;
|
||||
|
||||
case STATUS_ECC_UNCOR_ERROR:
|
||||
return -EBADMSG;
|
||||
|
||||
case STATUS_ECC_HAS_BITFLIPS:
|
||||
/*
|
||||
* Let's try to retrieve the real maximum number of bitflips
|
||||
* in order to avoid forcing the wear-leveling layer to move
|
||||
* data around if it's not necessary.
|
||||
*/
|
||||
if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr))
|
||||
return nand->eccreq.strength;
|
||||
|
||||
if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr))
|
||||
return nand->eccreq.strength;
|
||||
|
||||
return eccsr;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct spinand_info macronix_spinand_table[] = {
|
||||
SPINAND_INFO("MX35LF1GE4AB", 0x12,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
|
||||
mx35lf1ge4ab_ecc_get_status)),
|
||||
SPINAND_INFO("MX35LF2GE4AB", 0x22,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1),
|
||||
NAND_ECCREQ(4, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
|
||||
};
|
||||
|
||||
static int macronix_spinand_detect(struct spinand_device *spinand)
|
||||
{
|
||||
u8 *id = spinand->id.data;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Macronix SPI NAND read ID needs a dummy byte, so the first byte in
|
||||
* raw_id is garbage.
|
||||
*/
|
||||
if (id[1] != SPINAND_MFR_MACRONIX)
|
||||
return 0;
|
||||
|
||||
ret = spinand_match_and_init(spinand, macronix_spinand_table,
|
||||
ARRAY_SIZE(macronix_spinand_table),
|
||||
id[2]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = {
|
||||
.detect = macronix_spinand_detect,
|
||||
};
|
||||
|
||||
const struct spinand_manufacturer macronix_spinand_manufacturer = {
|
||||
.id = SPINAND_MFR_MACRONIX,
|
||||
.name = "Macronix",
|
||||
.ops = ¯onix_spinand_manuf_ops,
|
||||
};
|
133
drivers/mtd/nand/spi/micron.c
Normal file
133
drivers/mtd/nand/spi/micron.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Micron Technology, Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Peter Pan <peterpandong@micron.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/spinand.h>
|
||||
|
||||
#define SPINAND_MFR_MICRON 0x2c
|
||||
|
||||
#define MICRON_STATUS_ECC_MASK GENMASK(7, 4)
|
||||
#define MICRON_STATUS_ECC_NO_BITFLIPS (0 << 4)
|
||||
#define MICRON_STATUS_ECC_1TO3_BITFLIPS (1 << 4)
|
||||
#define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4)
|
||||
#define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4)
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(write_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(true, 0, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(update_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
|
||||
static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = 64;
|
||||
region->length = 64;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
/* Reserve 2 bytes for the BBM. */
|
||||
region->offset = 2;
|
||||
region->length = 62;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = {
|
||||
.ecc = mt29f2g01abagd_ooblayout_ecc,
|
||||
.free = mt29f2g01abagd_ooblayout_free,
|
||||
};
|
||||
|
||||
static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand,
|
||||
u8 status)
|
||||
{
|
||||
switch (status & MICRON_STATUS_ECC_MASK) {
|
||||
case STATUS_ECC_NO_BITFLIPS:
|
||||
return 0;
|
||||
|
||||
case STATUS_ECC_UNCOR_ERROR:
|
||||
return -EBADMSG;
|
||||
|
||||
case MICRON_STATUS_ECC_1TO3_BITFLIPS:
|
||||
return 3;
|
||||
|
||||
case MICRON_STATUS_ECC_4TO6_BITFLIPS:
|
||||
return 6;
|
||||
|
||||
case MICRON_STATUS_ECC_7TO8_BITFLIPS:
|
||||
return 8;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct spinand_info micron_spinand_table[] = {
|
||||
SPINAND_INFO("MT29F2G01ABAGD", 0x24,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout,
|
||||
mt29f2g01abagd_ecc_get_status)),
|
||||
};
|
||||
|
||||
static int micron_spinand_detect(struct spinand_device *spinand)
|
||||
{
|
||||
u8 *id = spinand->id.data;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Micron SPI NAND read ID need a dummy byte,
|
||||
* so the first byte in raw_id is dummy.
|
||||
*/
|
||||
if (id[1] != SPINAND_MFR_MICRON)
|
||||
return 0;
|
||||
|
||||
ret = spinand_match_and_init(spinand, micron_spinand_table,
|
||||
ARRAY_SIZE(micron_spinand_table), id[2]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {
|
||||
.detect = micron_spinand_detect,
|
||||
};
|
||||
|
||||
const struct spinand_manufacturer micron_spinand_manufacturer = {
|
||||
.id = SPINAND_MFR_MICRON,
|
||||
.name = "Micron",
|
||||
.ops = µn_spinand_manuf_ops,
|
||||
};
|
141
drivers/mtd/nand/spi/winbond.c
Normal file
141
drivers/mtd/nand/spi/winbond.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2017 exceet electronics GmbH
|
||||
*
|
||||
* Authors:
|
||||
* Frieder Schrempf <frieder.schrempf@exceet.de>
|
||||
* Boris Brezillon <boris.brezillon@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/spinand.h>
|
||||
|
||||
#define SPINAND_MFR_WINBOND 0xEF
|
||||
|
||||
#define WINBOND_CFG_BUF_READ BIT(3)
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(write_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(true, 0, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(update_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
|
||||
static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section > 3)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = (16 * section) + 8;
|
||||
region->length = 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section > 3)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = (16 * section) + 2;
|
||||
region->length = 6;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
|
||||
.ecc = w25m02gv_ooblayout_ecc,
|
||||
.free = w25m02gv_ooblayout_free,
|
||||
};
|
||||
|
||||
static int w25m02gv_select_target(struct spinand_device *spinand,
|
||||
unsigned int target)
|
||||
{
|
||||
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
|
||||
SPI_MEM_OP_NO_ADDR,
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_OUT(1,
|
||||
spinand->scratchbuf,
|
||||
1));
|
||||
|
||||
*spinand->scratchbuf = target;
|
||||
return spi_mem_exec_op(spinand->spimem, &op);
|
||||
}
|
||||
|
||||
static const struct spinand_info winbond_spinand_table[] = {
|
||||
SPINAND_INFO("W25M02GV", 0xAB,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
|
||||
SPINAND_SELECT_TARGET(w25m02gv_select_target)),
|
||||
};
|
||||
|
||||
/**
|
||||
* winbond_spinand_detect - initialize device related part in spinand_device
|
||||
* struct if it is a Winbond device.
|
||||
* @spinand: SPI NAND device structure
|
||||
*/
|
||||
static int winbond_spinand_detect(struct spinand_device *spinand)
|
||||
{
|
||||
u8 *id = spinand->id.data;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Winbond SPI NAND read ID need a dummy byte,
|
||||
* so the first byte in raw_id is dummy.
|
||||
*/
|
||||
if (id[1] != SPINAND_MFR_WINBOND)
|
||||
return 0;
|
||||
|
||||
ret = spinand_match_and_init(spinand, winbond_spinand_table,
|
||||
ARRAY_SIZE(winbond_spinand_table), id[2]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int winbond_spinand_init(struct spinand_device *spinand)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Make sure all dies are in buffer read mode and not continuous read
|
||||
* mode.
|
||||
*/
|
||||
for (i = 0; i < nand->memorg.ntargets; i++) {
|
||||
spinand_select_target(spinand, i);
|
||||
spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ,
|
||||
WINBOND_CFG_BUF_READ);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
|
||||
.detect = winbond_spinand_detect,
|
||||
.init = winbond_spinand_init,
|
||||
};
|
||||
|
||||
const struct spinand_manufacturer winbond_spinand_manufacturer = {
|
||||
.id = SPINAND_MFR_WINBOND,
|
||||
.name = "Winbond",
|
||||
.ops = &winbond_spinand_manuf_ops,
|
||||
};
|
|
@ -577,7 +577,7 @@ static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
|
|||
int NFTL_mount(struct NFTLrecord *s)
|
||||
{
|
||||
int i;
|
||||
unsigned int first_logical_block, logical_block, rep_block, nb_erases, erase_mark;
|
||||
unsigned int first_logical_block, logical_block, rep_block, erase_mark;
|
||||
unsigned int block, first_block, is_first_block;
|
||||
int chain_length, do_format_chain;
|
||||
struct nftl_uci0 h0;
|
||||
|
@ -621,7 +621,6 @@ int NFTL_mount(struct NFTLrecord *s)
|
|||
|
||||
logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum));
|
||||
rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum));
|
||||
nb_erases = le32_to_cpu (h1.WearInfo);
|
||||
erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
|
||||
|
||||
is_first_block = !(logical_block >> 15);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue