MTD changes:
Core: * Remove support for asynchronous erase (not implemented by any of the existing drivers anyway) * Remove Cyrille from the list of SPI NOR and MTD maintainers * Fix kernel doc headers * Allow users to define the partitions parsers they want to test through a DT property (compatible of the partitions subnode) * Remove the bfin-async-flash driver (the only architecture using it has been removed) * Fix pagetest test * Add extra checks in mtd_erase() * Simplify the MTD partition creation logic and get rid of mtd_add_device_partitions() Drivers: * Add endianness information to the physmap DT binding * Add Eon EN29LV400A IDs to JEDEC probe logic * Use %*ph where appropriate SPI NOR changes: Drivers: * Make fsl-quaspi assign different names to MTD devices connected to the same QSPI controller * Remove an unneeded driver.bus assigned in the fsl-qspi driver NAND changes: Core: * Prepare arrival of the SPI NAND subsystem by implementing a generic (interface-agnostic) layer to ease manipulation of NAND devices * Move onenand code base to the drivers/mtd/nand/ dir * Rework timing mode selection * Provide a generic way for NAND chip drivers to flag a specific GET/SET FEATURE operation as supported/unsupported * Stop embedding ONFI/JEDEC param page in nand_chip Drivers: * Rework/cleanup of the mxc driver * Various cleanups in the vf610 driver * Migrate the fsmc and vf610 to ->exec_op() * Get rid of the pxa driver (replaced by marvell_nand) * Support ->setup_data_interface() in the GPMI driver * Fix probe error path in several drivers * Remove support for unused hw_syndrome mode in sunxi_nand * Various minor improvements -----BEGIN PGP SIGNATURE----- iQI5BAABCAAjBQJaxTXfHBxib3Jpcy5icmV6aWxsb25AYm9vdGxpbi5jb20ACgkQ Ze02AX4ItwDeFhAAs+OmGip0LHk+D7gFCBONtypOGRi0nmGsi6PkhKtUZBob6/Y2 JoJhTrHx/LsRpqcuh3Y8KSCp0CG5WlZX2m1RU1yrcqiWvTfiXHlv6aQGfiE/esRb Ei4QNiND8hg+hyzc6I5wRrAuJ7jPP5BanX+n9TyvygaA1Ic5pcur0gssoYKeJTia 18pUV+RLe67wfP02uT0GJvmXd5ecIpu0OVhmJPye2UQqnfl+eiJ2zI4TJAZN3zRW tD9XiX3/GP8oCnvC+1kZw48a2c/qiHs/DKCdDcH6SDTKzKha4zCgaX4220X3AqPK rScrFTKxiCa1utZPn9EplnW6ZW3Ud46GqReWGRQZuyphu/ntdkNim7nuJMUZfEFL RFtMLhXDs1aXUaYJ6F3YQeajHxVU6Ugl34UQCraCXbfmzqCkaKfgXm3EVb9d2duY rCkrS5N4gSQXp5SMvc0aiu2TnsRX2OCthGg0NBdHG9AaVhwuj0neeSFQ/XFPxOeh 5jQKo4umR7tr2Md7osSx0PVxVo8uO5smqcl90SsdwrjxQxL7z1u/SJBjRf6pJDzQ cR1o+H9jBXecqD6m+dN3H/h0sMwHzefsASkxYNxl2OeRGYsJvBpuoQF8yUD9jnRS AzHhWhUNo8g3FxDe7kKgsMXoqnr/pKopMCRxVbeEkhDFNsTvBde10/Xo09U= =EG7L -----END PGP SIGNATURE----- Merge tag 'mtd/for-4.17' of git://git.infradead.org/linux-mtd Pull MTD updates from Boris Brezillon: "MTD Core: - Remove support for asynchronous erase (not implemented by any of the existing drivers anyway) - Remove Cyrille from the list of SPI NOR and MTD maintainers - Fix kernel doc headers - Allow users to define the partitions parsers they want to test through a DT property (compatible of the partitions subnode) - Remove the bfin-async-flash driver (the only architecture using it has been removed) - Fix pagetest test - Add extra checks in mtd_erase() - Simplify the MTD partition creation logic and get rid of mtd_add_device_partitions() MTD Drivers: - Add endianness information to the physmap DT binding - Add Eon EN29LV400A IDs to JEDEC probe logic - Use %*ph where appropriate SPI NOR Drivers: - Make fsl-quaspi assign different names to MTD devices connected to the same QSPI controller - Remove an unneeded driver.bus assigned in the fsl-qspi driver NAND Core: - Prepare arrival of the SPI NAND subsystem by implementing a generic (interface-agnostic) layer to ease manipulation of NAND devices - Move onenand code base to the drivers/mtd/nand/ dir - Rework timing mode selection - Provide a generic way for NAND chip drivers to flag a specific GET/SET FEATURE operation as supported/unsupported - Stop embedding ONFI/JEDEC param page in nand_chip NAND Drivers: - Rework/cleanup of the mxc driver - Various cleanups in the vf610 driver - Migrate the fsmc and vf610 to ->exec_op() - Get rid of the pxa driver (replaced by marvell_nand) - Support ->setup_data_interface() in the GPMI driver - Fix probe error path in several drivers - Remove support for unused hw_syndrome mode in sunxi_nand - Various minor improvements" * tag 'mtd/for-4.17' of git://git.infradead.org/linux-mtd: (89 commits) dt-bindings: fsl-quadspi: Add the example of two SPI NOR mtd: fsl-quadspi: Distinguish the mtd device names mtd: nand: Fix some function description mismatches in core.c mtd: fsl-quadspi: Remove unneeded driver.bus assignment mtd: rawnand: marvell: Rename ->ecc_clk into ->core_clk mtd: rawnand: s3c2410: enhance the probe function error path mtd: rawnand: tango: fix probe function error path mtd: rawnand: sh_flctl: fix the probe function error path mtd: rawnand: omap2: fix the probe function error path mtd: rawnand: mxc: fix probe function error path mtd: rawnand: denali: fix probe function error path mtd: rawnand: davinci: fix probe function error path mtd: rawnand: cafe: fix probe function error path mtd: rawnand: brcmnand: fix probe function error path mtd: rawnand: sunxi: Stop supporting ECC_HW_SYNDROME mode mtd: rawnand: marvell: Fix clock resource by adding a register clock mtd: ftl: Use DIV_ROUND_UP() mtd: Fix some function description mismatches in mtdcore.c mtd: physmap_of: update struct map_info's swap as per map requirement dt-bindings: mtd-physmap: Add endianness supports ...
This commit is contained in:
commit
3fd14cdcc0
7
.mailmap
7
.mailmap
|
@ -34,9 +34,9 @@ Axel Lin <axel.lin@gmail.com>
|
||||||
Ben Gardner <bgardner@wabtec.com>
|
Ben Gardner <bgardner@wabtec.com>
|
||||||
Ben M Cahill <ben.m.cahill@intel.com>
|
Ben M Cahill <ben.m.cahill@intel.com>
|
||||||
Björn Steinbrink <B.Steinbrink@gmx.de>
|
Björn Steinbrink <B.Steinbrink@gmx.de>
|
||||||
Boris Brezillon <boris.brezillon@free-electrons.com>
|
Boris Brezillon <boris.brezillon@bootlin.com> <boris.brezillon@free-electrons.com>
|
||||||
Boris Brezillon <boris.brezillon@free-electrons.com> <b.brezillon.dev@gmail.com>
|
Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon.dev@gmail.com>
|
||||||
Boris Brezillon <boris.brezillon@free-electrons.com> <b.brezillon@overkiz.com>
|
Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon@overkiz.com>
|
||||||
Brian Avery <b.avery@hp.com>
|
Brian Avery <b.avery@hp.com>
|
||||||
Brian King <brking@us.ibm.com>
|
Brian King <brking@us.ibm.com>
|
||||||
Christoph Hellwig <hch@lst.de>
|
Christoph Hellwig <hch@lst.de>
|
||||||
|
@ -128,6 +128,7 @@ Mayuresh Janorkar <mayur@ti.com>
|
||||||
Michael Buesch <m@bues.ch>
|
Michael Buesch <m@bues.ch>
|
||||||
Michel Dänzer <michel@tungstengraphics.com>
|
Michel Dänzer <michel@tungstengraphics.com>
|
||||||
Miodrag Dinic <miodrag.dinic@mips.com> <miodrag.dinic@imgtec.com>
|
Miodrag Dinic <miodrag.dinic@mips.com> <miodrag.dinic@imgtec.com>
|
||||||
|
Miquel Raynal <miquel.raynal@bootlin.com> <miquel.raynal@free-electrons.com>
|
||||||
Mitesh shah <mshah@teja.com>
|
Mitesh shah <mshah@teja.com>
|
||||||
Mohit Kumar <mohit.kumar@st.com> <mohit.kumar.dhaka@gmail.com>
|
Mohit Kumar <mohit.kumar@st.com> <mohit.kumar.dhaka@gmail.com>
|
||||||
Morten Welinder <terra@gnome.org>
|
Morten Welinder <terra@gnome.org>
|
||||||
|
|
|
@ -46,7 +46,7 @@ NAND
|
||||||
----
|
----
|
||||||
|
|
||||||
The NAND hardware is similar to the S3C2440, and is supported by the
|
The NAND hardware is similar to the S3C2440, and is supported by the
|
||||||
s3c2410 driver in the drivers/mtd/nand directory.
|
s3c2410 driver in the drivers/mtd/nand/raw directory.
|
||||||
|
|
||||||
|
|
||||||
USB Host
|
USB Host
|
||||||
|
|
|
@ -39,3 +39,27 @@ qspi0: quadspi@40044000 {
|
||||||
....
|
....
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Example showing the usage of two SPI NOR devices:
|
||||||
|
|
||||||
|
&qspi2 {
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&pinctrl_qspi2>;
|
||||||
|
status = "okay";
|
||||||
|
|
||||||
|
flash0: n25q256a@0 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
compatible = "micron,n25q256a", "jedec,spi-nor";
|
||||||
|
spi-max-frequency = <29000000>;
|
||||||
|
reg = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
flash1: n25q256a@1 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
compatible = "micron,n25q256a", "jedec,spi-nor";
|
||||||
|
spi-max-frequency = <29000000>;
|
||||||
|
reg = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -14,7 +14,10 @@ Required properties:
|
||||||
- #address-cells: shall be set to 1. Encode the NAND CS.
|
- #address-cells: shall be set to 1. Encode the NAND CS.
|
||||||
- #size-cells: shall be set to 0.
|
- #size-cells: shall be set to 0.
|
||||||
- interrupts: shall define the NAND controller interrupt.
|
- interrupts: shall define the NAND controller interrupt.
|
||||||
- clocks: shall reference the NAND controller clock.
|
- clocks: shall reference the NAND controller clocks, the second one is
|
||||||
|
is only needed for the Armada 7K/8K SoCs
|
||||||
|
- clock-names: mandatory if there is a second clock, in this case there
|
||||||
|
should be one clock named "core" and another one named "reg"
|
||||||
- marvell,system-controller: Set to retrieve the syscon node that handles
|
- marvell,system-controller: Set to retrieve the syscon node that handles
|
||||||
NAND controller related registers (only required with the
|
NAND controller related registers (only required with the
|
||||||
"marvell,armada-8k-nand[-controller]" compatibles).
|
"marvell,armada-8k-nand[-controller]" compatibles).
|
||||||
|
|
|
@ -41,6 +41,13 @@ additional (optional) property is defined:
|
||||||
|
|
||||||
- erase-size : The chip's physical erase block size in bytes.
|
- erase-size : The chip's physical erase block size in bytes.
|
||||||
|
|
||||||
|
The device tree may optionally contain endianness property.
|
||||||
|
little-endian or big-endian : It Represents the endianness that should be used
|
||||||
|
by the controller to properly read/write data
|
||||||
|
from/to the flash. If this property is missing,
|
||||||
|
the endianness is chosen by the system
|
||||||
|
(potentially based on extra configuration options).
|
||||||
|
|
||||||
The device tree may optionally contain sub-nodes describing partitions of the
|
The device tree may optionally contain sub-nodes describing partitions of the
|
||||||
address space. See partition.txt for more detail.
|
address space. See partition.txt for more detail.
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,8 @@ Optional properties:
|
||||||
- allwinner,rb : shall contain the native Ready/Busy ids.
|
- allwinner,rb : shall contain the native Ready/Busy ids.
|
||||||
or
|
or
|
||||||
- rb-gpios : shall contain the gpios used as R/B pins.
|
- rb-gpios : shall contain the gpios used as R/B pins.
|
||||||
- nand-ecc-mode : one of the supported ECC modes ("hw", "hw_syndrome", "soft",
|
- nand-ecc-mode : one of the supported ECC modes ("hw", "soft", "soft_bch" or
|
||||||
"soft_bch" or "none")
|
"none")
|
||||||
|
|
||||||
see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
|
see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
|
||||||
|
|
||||||
|
|
|
@ -75,8 +75,8 @@ hardware descriptions such as device tree or ACPI:
|
||||||
it from 1-to-0-to-1. If that hardware does not receive its "ping"
|
it from 1-to-0-to-1. If that hardware does not receive its "ping"
|
||||||
periodically, it will reset the system.
|
periodically, it will reset the system.
|
||||||
|
|
||||||
- gpio-nand: drivers/mtd/nand/gpio.c is used to connect a NAND flash chip to
|
- gpio-nand: drivers/mtd/nand/raw/gpio.c is used to connect a NAND flash chip
|
||||||
a set of simple GPIO lines: RDY, NCE, ALE, CLE, NWP. It interacts with the
|
to a set of simple GPIO lines: RDY, NCE, ALE, CLE, NWP. It interacts with the
|
||||||
NAND flash MTD subsystem and provides chip access and partition parsing like
|
NAND flash MTD subsystem and provides chip access and partition parsing like
|
||||||
any other NAND driving hardware.
|
any other NAND driving hardware.
|
||||||
|
|
||||||
|
|
|
@ -967,10 +967,10 @@ API functions which are exported. Each function has a short description
|
||||||
which is marked with an [XXX] identifier. See the chapter "Documentation
|
which is marked with an [XXX] identifier. See the chapter "Documentation
|
||||||
hints" for an explanation.
|
hints" for an explanation.
|
||||||
|
|
||||||
.. kernel-doc:: drivers/mtd/nand/nand_base.c
|
.. kernel-doc:: drivers/mtd/nand/raw/nand_base.c
|
||||||
:export:
|
:export:
|
||||||
|
|
||||||
.. kernel-doc:: drivers/mtd/nand/nand_ecc.c
|
.. kernel-doc:: drivers/mtd/nand/raw/nand_ecc.c
|
||||||
:export:
|
:export:
|
||||||
|
|
||||||
Internal Functions Provided
|
Internal Functions Provided
|
||||||
|
@ -982,10 +982,10 @@ marked with an [XXX] identifier. See the chapter "Documentation hints"
|
||||||
for an explanation. The functions marked with [DEFAULT] might be
|
for an explanation. The functions marked with [DEFAULT] might be
|
||||||
relevant for a board driver developer.
|
relevant for a board driver developer.
|
||||||
|
|
||||||
.. kernel-doc:: drivers/mtd/nand/nand_base.c
|
.. kernel-doc:: drivers/mtd/nand/raw/nand_base.c
|
||||||
:internal:
|
:internal:
|
||||||
|
|
||||||
.. kernel-doc:: drivers/mtd/nand/nand_bbt.c
|
.. kernel-doc:: drivers/mtd/nand/raw/nand_bbt.c
|
||||||
:internal:
|
:internal:
|
||||||
|
|
||||||
Credits
|
Credits
|
||||||
|
|
36
MAINTAINERS
36
MAINTAINERS
|
@ -1238,7 +1238,7 @@ F: arch/arm/boot/dts/aspeed-*
|
||||||
F: drivers/*/*aspeed*
|
F: drivers/*/*aspeed*
|
||||||
|
|
||||||
ARM/ATMEL AT91 Clock Support
|
ARM/ATMEL AT91 Clock Support
|
||||||
M: Boris Brezillon <boris.brezillon@free-electrons.com>
|
M: Boris Brezillon <boris.brezillon@bootlin.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/clk/at91
|
F: drivers/clk/at91
|
||||||
|
|
||||||
|
@ -1721,7 +1721,7 @@ F: drivers/input/keyboard/w90p910_keypad.c
|
||||||
F: drivers/input/touchscreen/w90p910_ts.c
|
F: drivers/input/touchscreen/w90p910_ts.c
|
||||||
F: drivers/watchdog/nuc900_wdt.c
|
F: drivers/watchdog/nuc900_wdt.c
|
||||||
F: drivers/net/ethernet/nuvoton/w90p910_ether.c
|
F: drivers/net/ethernet/nuvoton/w90p910_ether.c
|
||||||
F: drivers/mtd/nand/nuc900_nand.c
|
F: drivers/mtd/nand/raw/nuc900_nand.c
|
||||||
F: drivers/rtc/rtc-nuc900.c
|
F: drivers/rtc/rtc-nuc900.c
|
||||||
F: drivers/spi/spi-nuc900.c
|
F: drivers/spi/spi-nuc900.c
|
||||||
F: drivers/usb/host/ehci-w90x900.c
|
F: drivers/usb/host/ehci-w90x900.c
|
||||||
|
@ -2999,7 +2999,7 @@ M: Kamal Dasu <kdasu.kdev@gmail.com>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
L: bcm-kernel-feedback-list@broadcom.com
|
L: bcm-kernel-feedback-list@broadcom.com
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mtd/nand/brcmnand/
|
F: drivers/mtd/nand/raw/brcmnand/
|
||||||
|
|
||||||
BROADCOM STB DPFE DRIVER
|
BROADCOM STB DPFE DRIVER
|
||||||
M: Markus Mayer <mmayer@broadcom.com>
|
M: Markus Mayer <mmayer@broadcom.com>
|
||||||
|
@ -4091,7 +4091,7 @@ DENALI NAND DRIVER
|
||||||
M: Masahiro Yamada <yamada.masahiro@socionext.com>
|
M: Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/mtd/nand/denali*
|
F: drivers/mtd/nand/raw/denali*
|
||||||
|
|
||||||
DESIGNWARE USB2 DRD IP DRIVER
|
DESIGNWARE USB2 DRD IP DRIVER
|
||||||
M: Minas Harutyunyan <hminas@synopsys.com>
|
M: Minas Harutyunyan <hminas@synopsys.com>
|
||||||
|
@ -4633,7 +4633,7 @@ F: Documentation/gpu/meson.rst
|
||||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||||
|
|
||||||
DRM DRIVERS FOR ATMEL HLCDC
|
DRM DRIVERS FOR ATMEL HLCDC
|
||||||
M: Boris Brezillon <boris.brezillon@free-electrons.com>
|
M: Boris Brezillon <boris.brezillon@bootlin.com>
|
||||||
L: dri-devel@lists.freedesktop.org
|
L: dri-devel@lists.freedesktop.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/gpu/drm/atmel-hlcdc/
|
F: drivers/gpu/drm/atmel-hlcdc/
|
||||||
|
@ -5630,7 +5630,7 @@ FREESCALE GPMI NAND DRIVER
|
||||||
M: Han Xu <han.xu@nxp.com>
|
M: Han Xu <han.xu@nxp.com>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mtd/nand/gpmi-nand/*
|
F: drivers/mtd/nand/raw/gpmi-nand/*
|
||||||
|
|
||||||
FREESCALE I2C CPM DRIVER
|
FREESCALE I2C CPM DRIVER
|
||||||
M: Jochen Friedrich <jochen@scram.de>
|
M: Jochen Friedrich <jochen@scram.de>
|
||||||
|
@ -6943,7 +6943,7 @@ INGENIC JZ4780 NAND DRIVER
|
||||||
M: Harvey Hunt <harveyhuntnexus@gmail.com>
|
M: Harvey Hunt <harveyhuntnexus@gmail.com>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mtd/nand/jz4780_*
|
F: drivers/mtd/nand/raw/jz4780_*
|
||||||
|
|
||||||
INOTIFY
|
INOTIFY
|
||||||
M: Jan Kara <jack@suse.cz>
|
M: Jan Kara <jack@suse.cz>
|
||||||
|
@ -8431,7 +8431,7 @@ F: include/uapi/drm/armada_drm.h
|
||||||
F: Documentation/devicetree/bindings/display/armada/
|
F: Documentation/devicetree/bindings/display/armada/
|
||||||
|
|
||||||
MARVELL CRYPTO DRIVER
|
MARVELL CRYPTO DRIVER
|
||||||
M: Boris Brezillon <boris.brezillon@free-electrons.com>
|
M: Boris Brezillon <boris.brezillon@bootlin.com>
|
||||||
M: Arnaud Ebalard <arno@natisbad.org>
|
M: Arnaud Ebalard <arno@natisbad.org>
|
||||||
F: drivers/crypto/marvell/
|
F: drivers/crypto/marvell/
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
@ -8490,10 +8490,10 @@ S: Odd Fixes
|
||||||
F: drivers/net/wireless/marvell/mwl8k.c
|
F: drivers/net/wireless/marvell/mwl8k.c
|
||||||
|
|
||||||
MARVELL NAND CONTROLLER DRIVER
|
MARVELL NAND CONTROLLER DRIVER
|
||||||
M: Miquel Raynal <miquel.raynal@free-electrons.com>
|
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mtd/nand/marvell_nand.c
|
F: drivers/mtd/nand/raw/marvell_nand.c
|
||||||
F: Documentation/devicetree/bindings/mtd/marvell-nand.txt
|
F: Documentation/devicetree/bindings/mtd/marvell-nand.txt
|
||||||
|
|
||||||
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
|
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
|
||||||
|
@ -9098,10 +9098,9 @@ F: mm/
|
||||||
MEMORY TECHNOLOGY DEVICES (MTD)
|
MEMORY TECHNOLOGY DEVICES (MTD)
|
||||||
M: David Woodhouse <dwmw2@infradead.org>
|
M: David Woodhouse <dwmw2@infradead.org>
|
||||||
M: Brian Norris <computersforpeace@gmail.com>
|
M: Brian Norris <computersforpeace@gmail.com>
|
||||||
M: Boris Brezillon <boris.brezillon@free-electrons.com>
|
M: Boris Brezillon <boris.brezillon@bootlin.com>
|
||||||
M: Marek Vasut <marek.vasut@gmail.com>
|
M: Marek Vasut <marek.vasut@gmail.com>
|
||||||
M: Richard Weinberger <richard@nod.at>
|
M: Richard Weinberger <richard@nod.at>
|
||||||
M: Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
|
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
W: http://www.linux-mtd.infradead.org/
|
W: http://www.linux-mtd.infradead.org/
|
||||||
Q: http://patchwork.ozlabs.org/project/linux-mtd/list/
|
Q: http://patchwork.ozlabs.org/project/linux-mtd/list/
|
||||||
|
@ -9186,7 +9185,7 @@ M: Wenyou Yang <wenyou.yang@microchip.com>
|
||||||
M: Josh Wu <rainyfeeling@outlook.com>
|
M: Josh Wu <rainyfeeling@outlook.com>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/mtd/nand/atmel/*
|
F: drivers/mtd/nand/raw/atmel/*
|
||||||
F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
|
F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
|
||||||
|
|
||||||
MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
|
MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
|
||||||
|
@ -9518,7 +9517,7 @@ S: Supported
|
||||||
F: drivers/net/ethernet/myricom/myri10ge/
|
F: drivers/net/ethernet/myricom/myri10ge/
|
||||||
|
|
||||||
NAND FLASH SUBSYSTEM
|
NAND FLASH SUBSYSTEM
|
||||||
M: Boris Brezillon <boris.brezillon@free-electrons.com>
|
M: Boris Brezillon <boris.brezillon@bootlin.com>
|
||||||
R: Richard Weinberger <richard@nod.at>
|
R: Richard Weinberger <richard@nod.at>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
W: http://www.linux-mtd.infradead.org/
|
W: http://www.linux-mtd.infradead.org/
|
||||||
|
@ -10312,7 +10311,7 @@ ONENAND FLASH DRIVER
|
||||||
M: Kyungmin Park <kyungmin.park@samsung.com>
|
M: Kyungmin Park <kyungmin.park@samsung.com>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mtd/onenand/
|
F: drivers/mtd/nand/onenand/
|
||||||
F: include/linux/mtd/onenand*.h
|
F: include/linux/mtd/onenand*.h
|
||||||
|
|
||||||
ONSTREAM SCSI TAPE DRIVER
|
ONSTREAM SCSI TAPE DRIVER
|
||||||
|
@ -11921,8 +11920,8 @@ F: drivers/memstick/host/r592.*
|
||||||
RICOH SMARTMEDIA/XD DRIVER
|
RICOH SMARTMEDIA/XD DRIVER
|
||||||
M: Maxim Levitsky <maximlevitsky@gmail.com>
|
M: Maxim Levitsky <maximlevitsky@gmail.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mtd/nand/r852.c
|
F: drivers/mtd/nand/raw/r852.c
|
||||||
F: drivers/mtd/nand/r852.h
|
F: drivers/mtd/nand/raw/r852.h
|
||||||
|
|
||||||
RISC-V ARCHITECTURE
|
RISC-V ARCHITECTURE
|
||||||
M: Palmer Dabbelt <palmer@sifive.com>
|
M: Palmer Dabbelt <palmer@sifive.com>
|
||||||
|
@ -13118,7 +13117,6 @@ F: arch/arm/boot/dts/spear*
|
||||||
F: arch/arm/mach-spear/
|
F: arch/arm/mach-spear/
|
||||||
|
|
||||||
SPI NOR SUBSYSTEM
|
SPI NOR SUBSYSTEM
|
||||||
M: Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
|
|
||||||
M: Marek Vasut <marek.vasut@gmail.com>
|
M: Marek Vasut <marek.vasut@gmail.com>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
W: http://www.linux-mtd.infradead.org/
|
W: http://www.linux-mtd.infradead.org/
|
||||||
|
@ -14762,7 +14760,7 @@ VF610 NAND DRIVER
|
||||||
M: Stefan Agner <stefan@agner.ch>
|
M: Stefan Agner <stefan@agner.ch>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/mtd/nand/vf610_nfc.c
|
F: drivers/mtd/nand/raw/vf610_nfc.c
|
||||||
|
|
||||||
VFAT/FAT/MSDOS FILESYSTEM
|
VFAT/FAT/MSDOS FILESYSTEM
|
||||||
M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
|
M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
|
||||||
|
|
|
@ -333,8 +333,6 @@ source "drivers/mtd/devices/Kconfig"
|
||||||
|
|
||||||
source "drivers/mtd/nand/Kconfig"
|
source "drivers/mtd/nand/Kconfig"
|
||||||
|
|
||||||
source "drivers/mtd/onenand/Kconfig"
|
|
||||||
|
|
||||||
source "drivers/mtd/lpddr/Kconfig"
|
source "drivers/mtd/lpddr/Kconfig"
|
||||||
|
|
||||||
source "drivers/mtd/spi-nor/Kconfig"
|
source "drivers/mtd/spi-nor/Kconfig"
|
||||||
|
|
|
@ -32,7 +32,7 @@ obj-$(CONFIG_MTD_SWAP) += mtdswap.o
|
||||||
nftl-objs := nftlcore.o nftlmount.o
|
nftl-objs := nftlcore.o nftlmount.o
|
||||||
inftl-objs := inftlcore.o inftlmount.o
|
inftl-objs := inftlcore.o inftlmount.o
|
||||||
|
|
||||||
obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
|
obj-y += chips/ lpddr/ maps/ devices/ nand/ tests/
|
||||||
|
|
||||||
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
|
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
|
||||||
obj-$(CONFIG_MTD_UBI) += ubi/
|
obj-$(CONFIG_MTD_UBI) += ubi/
|
||||||
|
|
|
@ -1993,20 +1993,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||||
|
|
||||||
static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
{
|
{
|
||||||
unsigned long ofs, len;
|
return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr,
|
||||||
int ret;
|
instr->len, NULL);
|
||||||
|
|
||||||
ofs = instr->addr;
|
|
||||||
len = instr->len;
|
|
||||||
|
|
||||||
ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cfi_intelext_sync (struct mtd_info *mtd)
|
static void cfi_intelext_sync (struct mtd_info *mtd)
|
||||||
|
|
|
@ -2415,20 +2415,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||||
|
|
||||||
static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
{
|
{
|
||||||
unsigned long ofs, len;
|
return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr,
|
||||||
int ret;
|
instr->len, NULL);
|
||||||
|
|
||||||
ofs = instr->addr;
|
|
||||||
len = instr->len;
|
|
||||||
|
|
||||||
ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2436,7 +2424,6 @@ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
{
|
{
|
||||||
struct map_info *map = mtd->priv;
|
struct map_info *map = mtd->priv;
|
||||||
struct cfi_private *cfi = map->fldrv_priv;
|
struct cfi_private *cfi = map->fldrv_priv;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (instr->addr != 0)
|
if (instr->addr != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -2444,14 +2431,7 @@ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
if (instr->len != mtd->size)
|
if (instr->len != mtd->size)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = do_erase_chip(map, &cfi->chips[0]);
|
return do_erase_chip(map, &cfi->chips[0]);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_atmel_lock(struct map_info *map, struct flchip *chip,
|
static int do_atmel_lock(struct map_info *map, struct flchip *chip,
|
||||||
|
|
|
@ -965,9 +965,6 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,8 @@
|
||||||
#define AT49BV32XT 0x00C9
|
#define AT49BV32XT 0x00C9
|
||||||
|
|
||||||
/* Eon */
|
/* Eon */
|
||||||
|
#define EN29LV400AT 0x22B9
|
||||||
|
#define EN29LV400AB 0x22BA
|
||||||
#define EN29SL800BB 0x226B
|
#define EN29SL800BB 0x226B
|
||||||
#define EN29SL800BT 0x22EA
|
#define EN29SL800BT 0x22EA
|
||||||
|
|
||||||
|
@ -641,6 +643,36 @@ static const struct amd_flash_info jedec_table[] = {
|
||||||
ERASEINFO(0x10000,63),
|
ERASEINFO(0x10000,63),
|
||||||
ERASEINFO(0x02000,8)
|
ERASEINFO(0x02000,8)
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
.mfr_id = CFI_MFR_EON,
|
||||||
|
.dev_id = EN29LV400AT,
|
||||||
|
.name = "Eon EN29LV400AT",
|
||||||
|
.devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
|
||||||
|
.uaddr = MTD_UADDR_0x0AAA_0x0555,
|
||||||
|
.dev_size = SIZE_512KiB,
|
||||||
|
.cmd_set = P_ID_AMD_STD,
|
||||||
|
.nr_regions = 4,
|
||||||
|
.regions = {
|
||||||
|
ERASEINFO(0x10000,7),
|
||||||
|
ERASEINFO(0x08000,1),
|
||||||
|
ERASEINFO(0x02000,2),
|
||||||
|
ERASEINFO(0x04000,1),
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
.mfr_id = CFI_MFR_EON,
|
||||||
|
.dev_id = EN29LV400AB,
|
||||||
|
.name = "Eon EN29LV400AB",
|
||||||
|
.devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
|
||||||
|
.uaddr = MTD_UADDR_0x0AAA_0x0555,
|
||||||
|
.dev_size = SIZE_512KiB,
|
||||||
|
.cmd_set = P_ID_AMD_STD,
|
||||||
|
.nr_regions = 4,
|
||||||
|
.regions = {
|
||||||
|
ERASEINFO(0x04000,1),
|
||||||
|
ERASEINFO(0x02000,2),
|
||||||
|
ERASEINFO(0x08000,1),
|
||||||
|
ERASEINFO(0x10000,7),
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
.mfr_id = CFI_MFR_EON,
|
.mfr_id = CFI_MFR_EON,
|
||||||
.dev_id = EN29SL800BT,
|
.dev_id = EN29SL800BT,
|
||||||
|
|
|
@ -131,8 +131,6 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
|
||||||
allff = map_word_ff(map);
|
allff = map_word_ff(map);
|
||||||
for (i=0; i<instr->len; i += map_bankwidth(map))
|
for (i=0; i<instr->len; i += map_bankwidth(map))
|
||||||
map_write(map, allff, instr->addr + i);
|
map_write(map, allff, instr->addr + i);
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,6 @@ static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout)
|
||||||
static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase)
|
static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase)
|
||||||
{
|
{
|
||||||
struct bcm47xxsflash *b47s = mtd->priv;
|
struct bcm47xxsflash *b47s = mtd->priv;
|
||||||
int err;
|
|
||||||
|
|
||||||
switch (b47s->type) {
|
switch (b47s->type) {
|
||||||
case BCM47XXSFLASH_TYPE_ST:
|
case BCM47XXSFLASH_TYPE_ST:
|
||||||
|
@ -89,16 +88,7 @@ static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bcm47xxsflash_poll(b47s, HZ);
|
return bcm47xxsflash_poll(b47s, HZ);
|
||||||
if (err)
|
|
||||||
erase->state = MTD_ERASE_FAILED;
|
|
||||||
else
|
|
||||||
erase->state = MTD_ERASE_DONE;
|
|
||||||
|
|
||||||
if (erase->callback)
|
|
||||||
erase->callback(erase);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
|
|
|
@ -88,17 +88,12 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
size_t len = instr->len;
|
size_t len = instr->len;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
instr->state = MTD_ERASING;
|
|
||||||
mutex_lock(&dev->write_mutex);
|
mutex_lock(&dev->write_mutex);
|
||||||
err = _block2mtd_erase(dev, from, len);
|
err = _block2mtd_erase(dev, from, len);
|
||||||
mutex_unlock(&dev->write_mutex);
|
mutex_unlock(&dev->write_mutex);
|
||||||
if (err) {
|
if (err)
|
||||||
pr_err("erase failed err = %d\n", err);
|
pr_err("erase failed err = %d\n", err);
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
} else
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +220,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size,
|
||||||
int i;
|
int i;
|
||||||
#endif
|
#endif
|
||||||
const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
|
const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
|
||||||
struct block_device *bdev = ERR_PTR(-ENODEV);
|
struct block_device *bdev;
|
||||||
struct block2mtd_dev *dev;
|
struct block2mtd_dev *dev;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
|
|
|
@ -1191,39 +1191,27 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
|
||||||
{
|
{
|
||||||
struct docg3 *docg3 = mtd->priv;
|
struct docg3 *docg3 = mtd->priv;
|
||||||
uint64_t len;
|
uint64_t len;
|
||||||
int block0, block1, page, ret, ofs = 0;
|
int block0, block1, page, ret = 0, ofs = 0;
|
||||||
|
|
||||||
doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len);
|
doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len);
|
||||||
|
|
||||||
info->state = MTD_ERASE_PENDING;
|
|
||||||
calc_block_sector(info->addr + info->len, &block0, &block1, &page,
|
calc_block_sector(info->addr + info->len, &block0, &block1, &page,
|
||||||
&ofs, docg3->reliable);
|
&ofs, docg3->reliable);
|
||||||
ret = -EINVAL;
|
|
||||||
if (info->addr + info->len > mtd->size || page || ofs)
|
if (info->addr + info->len > mtd->size || page || ofs)
|
||||||
goto reset_err;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
calc_block_sector(info->addr, &block0, &block1, &page, &ofs,
|
calc_block_sector(info->addr, &block0, &block1, &page, &ofs,
|
||||||
docg3->reliable);
|
docg3->reliable);
|
||||||
mutex_lock(&docg3->cascade->lock);
|
mutex_lock(&docg3->cascade->lock);
|
||||||
doc_set_device_id(docg3, docg3->device_id);
|
doc_set_device_id(docg3, docg3->device_id);
|
||||||
doc_set_reliable_mode(docg3);
|
doc_set_reliable_mode(docg3);
|
||||||
for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
|
for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
|
||||||
info->state = MTD_ERASING;
|
|
||||||
ret = doc_erase_block(docg3, block0, block1);
|
ret = doc_erase_block(docg3, block0, block1);
|
||||||
block0 += 2;
|
block0 += 2;
|
||||||
block1 += 2;
|
block1 += 2;
|
||||||
}
|
}
|
||||||
mutex_unlock(&docg3->cascade->lock);
|
mutex_unlock(&docg3->cascade->lock);
|
||||||
|
|
||||||
if (ret)
|
|
||||||
goto reset_err;
|
|
||||||
|
|
||||||
info->state = MTD_ERASE_DONE;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
reset_err:
|
|
||||||
info->state = MTD_ERASE_FAILED;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -414,10 +414,7 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
|
||||||
while (len)
|
while (len)
|
||||||
{
|
{
|
||||||
if (!erase_block (addr))
|
if (!erase_block (addr))
|
||||||
{
|
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
return (-EIO);
|
return (-EIO);
|
||||||
}
|
|
||||||
|
|
||||||
addr += mtd->eraseregions[i].erasesize;
|
addr += mtd->eraseregions[i].erasesize;
|
||||||
len -= mtd->eraseregions[i].erasesize;
|
len -= mtd->eraseregions[i].erasesize;
|
||||||
|
@ -425,9 +422,6 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
|
||||||
if (addr == mtd->eraseregions[i].offset + (mtd->eraseregions[i].erasesize * mtd->eraseregions[i].numblocks)) i++;
|
if (addr == mtd->eraseregions[i].offset + (mtd->eraseregions[i].erasesize * mtd->eraseregions[i].numblocks)) i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -220,10 +220,6 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
}
|
}
|
||||||
mutex_unlock(&priv->lock);
|
mutex_unlock(&priv->lock);
|
||||||
|
|
||||||
/* Inform MTD subsystem that erase is complete */
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,8 +60,7 @@ static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
if (check_offs_len(mtd, instr->addr, instr->len))
|
if (check_offs_len(mtd, instr->addr, instr->len))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
|
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,13 +39,6 @@ static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
|
|
||||||
memset(start + instr->addr, 0xff, instr->len);
|
memset(start + instr->addr, 0xff, instr->len);
|
||||||
|
|
||||||
/*
|
|
||||||
* This'll catch a few races. Free the thing before returning :)
|
|
||||||
* I don't feel at all ashamed. This kind of thing is possible anyway
|
|
||||||
* with flash, but unlikely.
|
|
||||||
*/
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,12 +184,10 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
#ifdef CONFIG_MTD_PMC551_DEBUG
|
#ifdef CONFIG_MTD_PMC551_DEBUG
|
||||||
printk(KERN_DEBUG "pmc551_erase() done\n");
|
printk(KERN_DEBUG "pmc551_erase() done\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,19 +175,11 @@ static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
erase->state = MTD_ERASING;
|
|
||||||
|
|
||||||
/* todo: register our own notifier to do a true async implementation */
|
|
||||||
rc = powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr,
|
rc = powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr,
|
||||||
erase->len, NULL, NULL);
|
erase->len, NULL, NULL);
|
||||||
|
if (rc)
|
||||||
if (rc) {
|
|
||||||
erase->fail_addr = erase->addr;
|
erase->fail_addr = erase->addr;
|
||||||
erase->state = MTD_ERASE_FAILED;
|
|
||||||
} else {
|
|
||||||
erase->state = MTD_ERASE_DONE;
|
|
||||||
}
|
|
||||||
mtd_erase_callback(erase);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,12 +84,7 @@ static int slram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
slram_priv_t *priv = mtd->priv;
|
slram_priv_t *priv = mtd->priv;
|
||||||
|
|
||||||
memset(priv->start + instr->addr, 0xff, instr->len);
|
memset(priv->start + instr->addr, 0xff, instr->len);
|
||||||
/* This'll catch a few races. Free the thing before returning :)
|
|
||||||
* I don't feel at all ashamed. This kind of thing is possible anyway
|
|
||||||
* with flash, but unlikely.
|
|
||||||
*/
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -518,7 +518,6 @@ static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
|
||||||
/* preparing the command for flash */
|
/* preparing the command for flash */
|
||||||
ret = spear_smi_erase_sector(dev, bank, command, 4);
|
ret = spear_smi_erase_sector(dev, bank, command, 4);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
e_info->state = MTD_ERASE_FAILED;
|
|
||||||
mutex_unlock(&flash->lock);
|
mutex_unlock(&flash->lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -527,8 +526,6 @@ static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&flash->lock);
|
mutex_unlock(&flash->lock);
|
||||||
e_info->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(e_info);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
err = sst25l_erase_sector(flash, addr);
|
err = sst25l_erase_sector(flash, addr);
|
||||||
if (err) {
|
if (err) {
|
||||||
mutex_unlock(&flash->lock);
|
mutex_unlock(&flash->lock);
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
dev_err(&flash->spi->dev, "Erase failed\n");
|
dev_err(&flash->spi->dev, "Erase failed\n");
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -205,8 +204,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
|
|
||||||
mutex_unlock(&flash->lock);
|
mutex_unlock(&flash->lock);
|
||||||
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1825,13 +1825,9 @@ static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
|
|
||||||
mutex_unlock(&fsm->lock);
|
mutex_unlock(&fsm->lock);
|
||||||
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out1:
|
out1:
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
mutex_unlock(&fsm->lock);
|
mutex_unlock(&fsm->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1868,8 +1864,7 @@ static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm)
|
||||||
*/
|
*/
|
||||||
ext_jedec = id[3] << 8 | id[4];
|
ext_jedec = id[3] << 8 | id[4];
|
||||||
|
|
||||||
dev_dbg(fsm->dev, "JEDEC = 0x%08x [%02x %02x %02x %02x %02x]\n",
|
dev_dbg(fsm->dev, "JEDEC = 0x%08x [%5ph]\n", jedec, id);
|
||||||
jedec, id[0], id[1], id[2], id[3], id[4]);
|
|
||||||
|
|
||||||
for (info = flash_types; info->name; info++) {
|
for (info = flash_types; info->name; info++) {
|
||||||
if (info->jedec_id == jedec) {
|
if (info->jedec_id == jedec) {
|
||||||
|
|
|
@ -140,12 +140,6 @@ typedef struct partition_t {
|
||||||
#define XFER_PREPARED 0x03
|
#define XFER_PREPARED 0x03
|
||||||
#define XFER_FAILED 0x04
|
#define XFER_FAILED 0x04
|
||||||
|
|
||||||
/*====================================================================*/
|
|
||||||
|
|
||||||
|
|
||||||
static void ftl_erase_callback(struct erase_info *done);
|
|
||||||
|
|
||||||
|
|
||||||
/*======================================================================
|
/*======================================================================
|
||||||
|
|
||||||
Scan_header() checks to see if a memory region contains an FTL
|
Scan_header() checks to see if a memory region contains an FTL
|
||||||
|
@ -348,18 +342,19 @@ static int erase_xfer(partition_t *part,
|
||||||
if (!erase)
|
if (!erase)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
erase->mtd = part->mbd.mtd;
|
|
||||||
erase->callback = ftl_erase_callback;
|
|
||||||
erase->addr = xfer->Offset;
|
erase->addr = xfer->Offset;
|
||||||
erase->len = 1 << part->header.EraseUnitSize;
|
erase->len = 1 << part->header.EraseUnitSize;
|
||||||
erase->priv = (u_long)part;
|
|
||||||
|
|
||||||
ret = mtd_erase(part->mbd.mtd, erase);
|
ret = mtd_erase(part->mbd.mtd, erase);
|
||||||
|
if (!ret) {
|
||||||
|
xfer->state = XFER_ERASED;
|
||||||
|
xfer->EraseCount++;
|
||||||
|
} else {
|
||||||
|
xfer->state = XFER_FAILED;
|
||||||
|
pr_notice("ftl_cs: erase failed: err = %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ret)
|
kfree(erase);
|
||||||
xfer->EraseCount++;
|
|
||||||
else
|
|
||||||
kfree(erase);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
} /* erase_xfer */
|
} /* erase_xfer */
|
||||||
|
@ -371,37 +366,6 @@ static int erase_xfer(partition_t *part,
|
||||||
|
|
||||||
======================================================================*/
|
======================================================================*/
|
||||||
|
|
||||||
static void ftl_erase_callback(struct erase_info *erase)
|
|
||||||
{
|
|
||||||
partition_t *part;
|
|
||||||
struct xfer_info_t *xfer;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Look up the transfer unit */
|
|
||||||
part = (partition_t *)(erase->priv);
|
|
||||||
|
|
||||||
for (i = 0; i < part->header.NumTransferUnits; i++)
|
|
||||||
if (part->XferInfo[i].Offset == erase->addr) break;
|
|
||||||
|
|
||||||
if (i == part->header.NumTransferUnits) {
|
|
||||||
printk(KERN_NOTICE "ftl_cs: internal error: "
|
|
||||||
"erase lookup failed!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
xfer = &part->XferInfo[i];
|
|
||||||
if (erase->state == MTD_ERASE_DONE)
|
|
||||||
xfer->state = XFER_ERASED;
|
|
||||||
else {
|
|
||||||
xfer->state = XFER_FAILED;
|
|
||||||
printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
|
|
||||||
erase->state);
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(erase);
|
|
||||||
|
|
||||||
} /* ftl_erase_callback */
|
|
||||||
|
|
||||||
static int prepare_xfer(partition_t *part, int i)
|
static int prepare_xfer(partition_t *part, int i)
|
||||||
{
|
{
|
||||||
erase_unit_header_t header;
|
erase_unit_header_t header;
|
||||||
|
@ -429,8 +393,8 @@ static int prepare_xfer(partition_t *part, int i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write the BAM stub */
|
/* Write the BAM stub */
|
||||||
nbam = (part->BlocksPerUnit * sizeof(uint32_t) +
|
nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
|
||||||
le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
|
le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
|
||||||
|
|
||||||
offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
|
offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
|
||||||
ctl = cpu_to_le32(BLOCK_CONTROL);
|
ctl = cpu_to_le32(BLOCK_CONTROL);
|
||||||
|
|
|
@ -208,8 +208,6 @@ static int find_boot_record(struct INFTLrecord *inftl)
|
||||||
if (ip->Reserved0 != ip->firstUnit) {
|
if (ip->Reserved0 != ip->firstUnit) {
|
||||||
struct erase_info *instr = &inftl->instr;
|
struct erase_info *instr = &inftl->instr;
|
||||||
|
|
||||||
instr->mtd = inftl->mbd.mtd;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Most likely this is using the
|
* Most likely this is using the
|
||||||
* undocumented qiuck mount feature.
|
* undocumented qiuck mount feature.
|
||||||
|
@ -385,7 +383,6 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
|
||||||
_first_? */
|
_first_? */
|
||||||
|
|
||||||
/* Use async erase interface, test return code */
|
/* Use async erase interface, test return code */
|
||||||
instr->mtd = inftl->mbd.mtd;
|
|
||||||
instr->addr = block * inftl->EraseSize;
|
instr->addr = block * inftl->EraseSize;
|
||||||
instr->len = inftl->mbd.mtd->erasesize;
|
instr->len = inftl->mbd.mtd->erasesize;
|
||||||
/* Erase one physical eraseblock at a time, even though the NAND api
|
/* Erase one physical eraseblock at a time, even though the NAND api
|
||||||
|
@ -393,9 +390,10 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
|
||||||
mark only the failed block in the bbt. */
|
mark only the failed block in the bbt. */
|
||||||
for (physblock = 0; physblock < inftl->EraseSize;
|
for (physblock = 0; physblock < inftl->EraseSize;
|
||||||
physblock += instr->len, instr->addr += instr->len) {
|
physblock += instr->len, instr->addr += instr->len) {
|
||||||
mtd_erase(inftl->mbd.mtd, instr);
|
int ret;
|
||||||
|
|
||||||
if (instr->state == MTD_ERASE_FAILED) {
|
ret = mtd_erase(inftl->mbd.mtd, instr);
|
||||||
|
if (ret) {
|
||||||
printk(KERN_WARNING "INFTL: error while formatting block %d\n",
|
printk(KERN_WARNING "INFTL: error while formatting block %d\n",
|
||||||
block);
|
block);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
|
@ -380,14 +380,8 @@ out:
|
||||||
*/
|
*/
|
||||||
static int lpddr2_nvm_erase(struct mtd_info *mtd, struct erase_info *instr)
|
static int lpddr2_nvm_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
{
|
{
|
||||||
int ret = lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len,
|
return lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len,
|
||||||
LPDDR2_NVM_ERASE);
|
LPDDR2_NVM_ERASE);
|
||||||
if (!ret) {
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -693,8 +693,6 @@ static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
ofs += size;
|
ofs += size;
|
||||||
len -= size;
|
len -= size;
|
||||||
}
|
}
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -334,16 +334,6 @@ config MTD_PCMCIA_ANONYMOUS
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
config MTD_BFIN_ASYNC
|
|
||||||
tristate "Blackfin BF533-STAMP Flash Chip Support"
|
|
||||||
depends on BFIN533_STAMP && MTD_CFI && MTD_COMPLEX_MAPPINGS
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
Map driver which allows for simultaneous utilization of
|
|
||||||
ethernet and CFI parallel flash.
|
|
||||||
|
|
||||||
If compiled as a module, it will be called bfin-async-flash.
|
|
||||||
|
|
||||||
config MTD_GPIO_ADDR
|
config MTD_GPIO_ADDR
|
||||||
tristate "GPIO-assisted Flash Chip Support"
|
tristate "GPIO-assisted Flash Chip Support"
|
||||||
depends on GPIOLIB || COMPILE_TEST
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
|
|
|
@ -42,7 +42,6 @@ obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
|
||||||
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
|
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
|
||||||
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
|
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
|
||||||
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
|
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
|
||||||
obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
|
|
||||||
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
|
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
|
||||||
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
|
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
|
||||||
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
|
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
|
||||||
|
|
|
@ -1,196 +0,0 @@
|
||||||
/*
|
|
||||||
* drivers/mtd/maps/bfin-async-flash.c
|
|
||||||
*
|
|
||||||
* Handle the case where flash memory and ethernet mac/phy are
|
|
||||||
* mapped onto the same async bank. The BF533-STAMP does this
|
|
||||||
* for example. All board-specific configuration goes in your
|
|
||||||
* board resources file.
|
|
||||||
*
|
|
||||||
* Copyright 2000 Nicolas Pitre <nico@fluxnic.net>
|
|
||||||
* Copyright 2005-2008 Analog Devices Inc.
|
|
||||||
*
|
|
||||||
* Enter bugs at http://blackfin.uclinux.org/
|
|
||||||
*
|
|
||||||
* Licensed under the GPL-2 or later.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/mtd/mtd.h>
|
|
||||||
#include <linux/mtd/map.h>
|
|
||||||
#include <linux/mtd/partitions.h>
|
|
||||||
#include <linux/mtd/physmap.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
|
|
||||||
#include <asm/blackfin.h>
|
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <asm/unaligned.h>
|
|
||||||
|
|
||||||
#define pr_devinit(fmt, args...) \
|
|
||||||
({ static const char __fmt[] = fmt; printk(__fmt, ## args); })
|
|
||||||
|
|
||||||
#define DRIVER_NAME "bfin-async-flash"
|
|
||||||
|
|
||||||
struct async_state {
|
|
||||||
struct mtd_info *mtd;
|
|
||||||
struct map_info map;
|
|
||||||
int enet_flash_pin;
|
|
||||||
uint32_t flash_ambctl0, flash_ambctl1;
|
|
||||||
uint32_t save_ambctl0, save_ambctl1;
|
|
||||||
unsigned long irq_flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void switch_to_flash(struct async_state *state)
|
|
||||||
{
|
|
||||||
local_irq_save(state->irq_flags);
|
|
||||||
|
|
||||||
gpio_set_value(state->enet_flash_pin, 0);
|
|
||||||
|
|
||||||
state->save_ambctl0 = bfin_read_EBIU_AMBCTL0();
|
|
||||||
state->save_ambctl1 = bfin_read_EBIU_AMBCTL1();
|
|
||||||
bfin_write_EBIU_AMBCTL0(state->flash_ambctl0);
|
|
||||||
bfin_write_EBIU_AMBCTL1(state->flash_ambctl1);
|
|
||||||
SSYNC();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void switch_back(struct async_state *state)
|
|
||||||
{
|
|
||||||
bfin_write_EBIU_AMBCTL0(state->save_ambctl0);
|
|
||||||
bfin_write_EBIU_AMBCTL1(state->save_ambctl1);
|
|
||||||
SSYNC();
|
|
||||||
|
|
||||||
gpio_set_value(state->enet_flash_pin, 1);
|
|
||||||
|
|
||||||
local_irq_restore(state->irq_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static map_word bfin_flash_read(struct map_info *map, unsigned long ofs)
|
|
||||||
{
|
|
||||||
struct async_state *state = (struct async_state *)map->map_priv_1;
|
|
||||||
uint16_t word;
|
|
||||||
map_word test;
|
|
||||||
|
|
||||||
switch_to_flash(state);
|
|
||||||
|
|
||||||
word = readw(map->virt + ofs);
|
|
||||||
|
|
||||||
switch_back(state);
|
|
||||||
|
|
||||||
test.x[0] = word;
|
|
||||||
return test;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bfin_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
|
|
||||||
{
|
|
||||||
struct async_state *state = (struct async_state *)map->map_priv_1;
|
|
||||||
|
|
||||||
switch_to_flash(state);
|
|
||||||
|
|
||||||
memcpy(to, map->virt + from, len);
|
|
||||||
|
|
||||||
switch_back(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bfin_flash_write(struct map_info *map, map_word d1, unsigned long ofs)
|
|
||||||
{
|
|
||||||
struct async_state *state = (struct async_state *)map->map_priv_1;
|
|
||||||
uint16_t d;
|
|
||||||
|
|
||||||
d = d1.x[0];
|
|
||||||
|
|
||||||
switch_to_flash(state);
|
|
||||||
|
|
||||||
writew(d, map->virt + ofs);
|
|
||||||
SSYNC();
|
|
||||||
|
|
||||||
switch_back(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
|
|
||||||
{
|
|
||||||
struct async_state *state = (struct async_state *)map->map_priv_1;
|
|
||||||
|
|
||||||
switch_to_flash(state);
|
|
||||||
|
|
||||||
memcpy(map->virt + to, from, len);
|
|
||||||
SSYNC();
|
|
||||||
|
|
||||||
switch_back(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * const part_probe_types[] = {
|
|
||||||
"cmdlinepart", "RedBoot", NULL };
|
|
||||||
|
|
||||||
static int bfin_flash_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct physmap_flash_data *pdata = dev_get_platdata(&pdev->dev);
|
|
||||||
struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
||||||
struct async_state *state;
|
|
||||||
|
|
||||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
|
||||||
if (!state)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
state->map.name = DRIVER_NAME;
|
|
||||||
state->map.read = bfin_flash_read;
|
|
||||||
state->map.copy_from = bfin_flash_copy_from;
|
|
||||||
state->map.write = bfin_flash_write;
|
|
||||||
state->map.copy_to = bfin_flash_copy_to;
|
|
||||||
state->map.bankwidth = pdata->width;
|
|
||||||
state->map.size = resource_size(memory);
|
|
||||||
state->map.virt = (void __iomem *)memory->start;
|
|
||||||
state->map.phys = memory->start;
|
|
||||||
state->map.map_priv_1 = (unsigned long)state;
|
|
||||||
state->enet_flash_pin = platform_get_irq(pdev, 0);
|
|
||||||
state->flash_ambctl0 = flash_ambctl->start;
|
|
||||||
state->flash_ambctl1 = flash_ambctl->end;
|
|
||||||
|
|
||||||
if (gpio_request(state->enet_flash_pin, DRIVER_NAME)) {
|
|
||||||
pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->enet_flash_pin);
|
|
||||||
kfree(state);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
gpio_direction_output(state->enet_flash_pin, 1);
|
|
||||||
|
|
||||||
pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8);
|
|
||||||
state->mtd = do_map_probe(memory->name, &state->map);
|
|
||||||
if (!state->mtd) {
|
|
||||||
gpio_free(state->enet_flash_pin);
|
|
||||||
kfree(state);
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
mtd_device_parse_register(state->mtd, part_probe_types, NULL,
|
|
||||||
pdata->parts, pdata->nr_parts);
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, state);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bfin_flash_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct async_state *state = platform_get_drvdata(pdev);
|
|
||||||
gpio_free(state->enet_flash_pin);
|
|
||||||
mtd_device_unregister(state->mtd);
|
|
||||||
map_destroy(state->mtd);
|
|
||||||
kfree(state);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct platform_driver bfin_flash_driver = {
|
|
||||||
.probe = bfin_flash_probe,
|
|
||||||
.remove = bfin_flash_remove,
|
|
||||||
.driver = {
|
|
||||||
.name = DRIVER_NAME,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
module_platform_driver(bfin_flash_driver);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_DESCRIPTION("MTD map driver for Blackfins with flash/ethernet on same async bank");
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/mtd/map.h>
|
#include <linux/mtd/map.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
#include <linux/mtd/concat.h>
|
#include <linux/mtd/concat.h>
|
||||||
|
#include <linux/mtd/cfi_endian.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
@ -233,6 +234,11 @@ static int of_flash_probe(struct platform_device *dev)
|
||||||
info->list[i].map.bankwidth = be32_to_cpup(width);
|
info->list[i].map.bankwidth = be32_to_cpup(width);
|
||||||
info->list[i].map.device_node = dp;
|
info->list[i].map.device_node = dp;
|
||||||
|
|
||||||
|
if (of_property_read_bool(dp, "big-endian"))
|
||||||
|
info->list[i].map.swap = CFI_BIG_ENDIAN;
|
||||||
|
else if (of_property_read_bool(dp, "little-endian"))
|
||||||
|
info->list[i].map.swap = CFI_LITTLE_ENDIAN;
|
||||||
|
|
||||||
err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
|
err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
|
@ -55,48 +55,27 @@ struct mtdblk_dev {
|
||||||
* being written to until a different sector is required.
|
* being written to until a different sector is required.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void erase_callback(struct erase_info *done)
|
|
||||||
{
|
|
||||||
wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
|
|
||||||
wake_up(wait_q);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int erase_write (struct mtd_info *mtd, unsigned long pos,
|
static int erase_write (struct mtd_info *mtd, unsigned long pos,
|
||||||
int len, const char *buf)
|
int len, const char *buf)
|
||||||
{
|
{
|
||||||
struct erase_info erase;
|
struct erase_info erase;
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
|
||||||
wait_queue_head_t wait_q;
|
|
||||||
size_t retlen;
|
size_t retlen;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First, let's erase the flash block.
|
* First, let's erase the flash block.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
init_waitqueue_head(&wait_q);
|
|
||||||
erase.mtd = mtd;
|
|
||||||
erase.callback = erase_callback;
|
|
||||||
erase.addr = pos;
|
erase.addr = pos;
|
||||||
erase.len = len;
|
erase.len = len;
|
||||||
erase.priv = (u_long)&wait_q;
|
|
||||||
|
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
add_wait_queue(&wait_q, &wait);
|
|
||||||
|
|
||||||
ret = mtd_erase(mtd, &erase);
|
ret = mtd_erase(mtd, &erase);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
set_current_state(TASK_RUNNING);
|
|
||||||
remove_wait_queue(&wait_q, &wait);
|
|
||||||
printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
|
printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
|
||||||
"on \"%s\" failed\n",
|
"on \"%s\" failed\n",
|
||||||
pos, len, mtd->name);
|
pos, len, mtd->name);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule(); /* Wait for erase to finish. */
|
|
||||||
remove_wait_queue(&wait_q, &wait);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Next, write the data to flash.
|
* Next, write the data to flash.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -324,10 +324,6 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c
|
||||||
IOCTL calls for getting device parameters.
|
IOCTL calls for getting device parameters.
|
||||||
|
|
||||||
======================================================================*/
|
======================================================================*/
|
||||||
static void mtdchar_erase_callback (struct erase_info *instr)
|
|
||||||
{
|
|
||||||
wake_up((wait_queue_head_t *)instr->priv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
|
static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
|
||||||
{
|
{
|
||||||
|
@ -709,11 +705,6 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||||
if (!erase)
|
if (!erase)
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
else {
|
else {
|
||||||
wait_queue_head_t waitq;
|
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
|
||||||
|
|
||||||
init_waitqueue_head(&waitq);
|
|
||||||
|
|
||||||
if (cmd == MEMERASE64) {
|
if (cmd == MEMERASE64) {
|
||||||
struct erase_info_user64 einfo64;
|
struct erase_info_user64 einfo64;
|
||||||
|
|
||||||
|
@ -735,31 +726,8 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
||||||
erase->addr = einfo32.start;
|
erase->addr = einfo32.start;
|
||||||
erase->len = einfo32.length;
|
erase->len = einfo32.length;
|
||||||
}
|
}
|
||||||
erase->mtd = mtd;
|
|
||||||
erase->callback = mtdchar_erase_callback;
|
|
||||||
erase->priv = (unsigned long)&waitq;
|
|
||||||
|
|
||||||
/*
|
|
||||||
FIXME: Allow INTERRUPTIBLE. Which means
|
|
||||||
not having the wait_queue head on the stack.
|
|
||||||
|
|
||||||
If the wq_head is on the stack, and we
|
|
||||||
leave because we got interrupted, then the
|
|
||||||
wq_head is no longer there when the
|
|
||||||
callback routine tries to wake us up.
|
|
||||||
*/
|
|
||||||
ret = mtd_erase(mtd, erase);
|
ret = mtd_erase(mtd, erase);
|
||||||
if (!ret) {
|
|
||||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
||||||
add_wait_queue(&waitq, &wait);
|
|
||||||
if (erase->state != MTD_ERASE_DONE &&
|
|
||||||
erase->state != MTD_ERASE_FAILED)
|
|
||||||
schedule();
|
|
||||||
remove_wait_queue(&waitq, &wait);
|
|
||||||
set_current_state(TASK_RUNNING);
|
|
||||||
|
|
||||||
ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
|
|
||||||
}
|
|
||||||
kfree(erase);
|
kfree(erase);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -333,45 +333,6 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void concat_erase_callback(struct erase_info *instr)
|
|
||||||
{
|
|
||||||
wake_up((wait_queue_head_t *) instr->priv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
wait_queue_head_t waitq;
|
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This code was stol^H^H^H^Hinspired by mtdchar.c
|
|
||||||
*/
|
|
||||||
init_waitqueue_head(&waitq);
|
|
||||||
|
|
||||||
erase->mtd = mtd;
|
|
||||||
erase->callback = concat_erase_callback;
|
|
||||||
erase->priv = (unsigned long) &waitq;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: Allow INTERRUPTIBLE. Which means
|
|
||||||
* not having the wait_queue head on the stack.
|
|
||||||
*/
|
|
||||||
err = mtd_erase(mtd, erase);
|
|
||||||
if (!err) {
|
|
||||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
||||||
add_wait_queue(&waitq, &wait);
|
|
||||||
if (erase->state != MTD_ERASE_DONE
|
|
||||||
&& erase->state != MTD_ERASE_FAILED)
|
|
||||||
schedule();
|
|
||||||
remove_wait_queue(&waitq, &wait);
|
|
||||||
set_current_state(TASK_RUNNING);
|
|
||||||
|
|
||||||
err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
|
static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
{
|
{
|
||||||
struct mtd_concat *concat = CONCAT(mtd);
|
struct mtd_concat *concat = CONCAT(mtd);
|
||||||
|
@ -466,7 +427,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
erase->len = length;
|
erase->len = length;
|
||||||
|
|
||||||
length -= erase->len;
|
length -= erase->len;
|
||||||
if ((err = concat_dev_erase(subdev, erase))) {
|
if ((err = mtd_erase(subdev, erase))) {
|
||||||
/* sanity check: should never happen since
|
/* sanity check: should never happen since
|
||||||
* block alignment has been checked above */
|
* block alignment has been checked above */
|
||||||
BUG_ON(err == -EINVAL);
|
BUG_ON(err == -EINVAL);
|
||||||
|
@ -485,14 +446,9 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
erase->addr = 0;
|
erase->addr = 0;
|
||||||
offset += subdev->size;
|
offset += subdev->size;
|
||||||
}
|
}
|
||||||
instr->state = erase->state;
|
|
||||||
kfree(erase);
|
kfree(erase);
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
if (instr->callback)
|
return err;
|
||||||
instr->callback(instr);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||||
|
|
|
@ -419,7 +419,7 @@ int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
|
||||||
EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info);
|
EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mtd_wunit_to_pairing_info - get wunit from pairing information
|
* mtd_pairing_info_to_wunit - get wunit from pairing information
|
||||||
* @mtd: pointer to new MTD device info structure
|
* @mtd: pointer to new MTD device info structure
|
||||||
* @info: pairing information struct
|
* @info: pairing information struct
|
||||||
*
|
*
|
||||||
|
@ -641,29 +641,6 @@ out_error:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mtd_add_device_partitions(struct mtd_info *mtd,
|
|
||||||
struct mtd_partitions *parts)
|
|
||||||
{
|
|
||||||
const struct mtd_partition *real_parts = parts->parts;
|
|
||||||
int nbparts = parts->nr_parts;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
|
|
||||||
ret = add_mtd_device(mtd);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbparts > 0) {
|
|
||||||
ret = add_mtd_partitions(mtd, real_parts, nbparts);
|
|
||||||
if (ret && IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
|
|
||||||
del_mtd_device(mtd);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set a few defaults based on the parent devices, if not provided by the
|
* Set a few defaults based on the parent devices, if not provided by the
|
||||||
* driver
|
* driver
|
||||||
|
@ -696,14 +673,13 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd)
|
||||||
* 'parse_mtd_partitions()') and MTD device and partitions registering. It
|
* 'parse_mtd_partitions()') and MTD device and partitions registering. It
|
||||||
* basically follows the most common pattern found in many MTD drivers:
|
* basically follows the most common pattern found in many MTD drivers:
|
||||||
*
|
*
|
||||||
* * It first tries to probe partitions on MTD device @mtd using parsers
|
* * If the MTD_PARTITIONED_MASTER option is set, then the device as a whole is
|
||||||
|
* registered first.
|
||||||
|
* * Then It tries to probe partitions on MTD device @mtd using parsers
|
||||||
* specified in @types (if @types is %NULL, then the default list of parsers
|
* specified in @types (if @types is %NULL, then the default list of parsers
|
||||||
* is used, see 'parse_mtd_partitions()' for more information). If none are
|
* is used, see 'parse_mtd_partitions()' for more information). If none are
|
||||||
* found this functions tries to fallback to information specified in
|
* found this functions tries to fallback to information specified in
|
||||||
* @parts/@nr_parts.
|
* @parts/@nr_parts.
|
||||||
* * If any partitioning info was found, this function registers the found
|
|
||||||
* partitions. If the MTD_PARTITIONED_MASTER option is set, then the device
|
|
||||||
* as a whole is registered first.
|
|
||||||
* * If no partitions were found this function just registers the MTD device
|
* * If no partitions were found this function just registers the MTD device
|
||||||
* @mtd and exits.
|
* @mtd and exits.
|
||||||
*
|
*
|
||||||
|
@ -714,29 +690,31 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
|
||||||
const struct mtd_partition *parts,
|
const struct mtd_partition *parts,
|
||||||
int nr_parts)
|
int nr_parts)
|
||||||
{
|
{
|
||||||
struct mtd_partitions parsed;
|
struct mtd_partitions parsed = { };
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mtd_set_dev_defaults(mtd);
|
mtd_set_dev_defaults(mtd);
|
||||||
|
|
||||||
memset(&parsed, 0, sizeof(parsed));
|
if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
|
||||||
|
ret = add_mtd_device(mtd);
|
||||||
ret = parse_mtd_partitions(mtd, types, &parsed, parser_data);
|
if (ret)
|
||||||
if ((ret < 0 || parsed.nr_parts == 0) && parts && nr_parts) {
|
return ret;
|
||||||
/* Fall back to driver-provided partitions */
|
|
||||||
parsed = (struct mtd_partitions){
|
|
||||||
.parts = parts,
|
|
||||||
.nr_parts = nr_parts,
|
|
||||||
};
|
|
||||||
} else if (ret < 0) {
|
|
||||||
/* Didn't come up with parsed OR fallback partitions */
|
|
||||||
pr_info("mtd: failed to find partitions; one or more parsers reports errors (%d)\n",
|
|
||||||
ret);
|
|
||||||
/* Don't abort on errors; we can still use unpartitioned MTD */
|
|
||||||
memset(&parsed, 0, sizeof(parsed));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mtd_add_device_partitions(mtd, &parsed);
|
/* Prefer parsed partitions over driver-provided fallback */
|
||||||
|
ret = parse_mtd_partitions(mtd, types, &parsed, parser_data);
|
||||||
|
if (!ret && parsed.nr_parts) {
|
||||||
|
parts = parsed.parts;
|
||||||
|
nr_parts = parsed.nr_parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr_parts)
|
||||||
|
ret = add_mtd_partitions(mtd, parts, nr_parts);
|
||||||
|
else if (!device_is_registered(&mtd->dev))
|
||||||
|
ret = add_mtd_device(mtd);
|
||||||
|
else
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -758,6 +736,9 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
|
||||||
out:
|
out:
|
||||||
/* Cleanup any parsed partitions */
|
/* Cleanup any parsed partitions */
|
||||||
mtd_part_parser_cleanup(&parsed);
|
mtd_part_parser_cleanup(&parsed);
|
||||||
|
if (ret && device_is_registered(&mtd->dev))
|
||||||
|
del_mtd_device(mtd);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mtd_device_parse_register);
|
EXPORT_SYMBOL_GPL(mtd_device_parse_register);
|
||||||
|
@ -963,24 +944,25 @@ void __put_mtd_device(struct mtd_info *mtd)
|
||||||
EXPORT_SYMBOL_GPL(__put_mtd_device);
|
EXPORT_SYMBOL_GPL(__put_mtd_device);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Erase is an asynchronous operation. Device drivers are supposed
|
* Erase is an synchronous operation. Device drivers are epected to return a
|
||||||
* to call instr->callback() whenever the operation completes, even
|
* negative error code if the operation failed and update instr->fail_addr
|
||||||
* if it completes with a failure.
|
* to point the portion that was not properly erased.
|
||||||
* Callers are supposed to pass a callback function and wait for it
|
|
||||||
* to be called before writing to the block.
|
|
||||||
*/
|
*/
|
||||||
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
{
|
{
|
||||||
|
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||||
|
|
||||||
|
if (!mtd->erasesize || !mtd->_erase)
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr)
|
if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!(mtd->flags & MTD_WRITEABLE))
|
if (!(mtd->flags & MTD_WRITEABLE))
|
||||||
return -EROFS;
|
return -EROFS;
|
||||||
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
|
||||||
if (!instr->len) {
|
if (!instr->len)
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
ledtrig_mtd_activity();
|
ledtrig_mtd_activity();
|
||||||
return mtd->_erase(mtd, instr);
|
return mtd->_erase(mtd, instr);
|
||||||
}
|
}
|
||||||
|
@ -1525,9 +1507,9 @@ int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
|
||||||
EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
|
EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer
|
* mtd_ooblayout_set_databytes - set data bytes into the oob buffer
|
||||||
* @mtd: mtd info structure
|
* @mtd: mtd info structure
|
||||||
* @eccbuf: source buffer to get data bytes from
|
* @databuf: source buffer to get data bytes from
|
||||||
* @oobbuf: OOB buffer
|
* @oobbuf: OOB buffer
|
||||||
* @start: first ECC byte to set
|
* @start: first ECC byte to set
|
||||||
* @nbytes: number of ECC bytes to set
|
* @nbytes: number of ECC bytes to set
|
||||||
|
@ -1559,7 +1541,7 @@ int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
|
||||||
EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
|
EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB
|
* mtd_ooblayout_count_eccbytes - count the number of ECC bytes in OOB
|
||||||
* @mtd: mtd info structure
|
* @mtd: mtd info structure
|
||||||
*
|
*
|
||||||
* Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
|
* Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
|
||||||
|
|
|
@ -84,12 +84,6 @@ static int page_is_used(struct mtdoops_context *cxt, int page)
|
||||||
return test_bit(page, cxt->oops_page_used);
|
return test_bit(page, cxt->oops_page_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtdoops_erase_callback(struct erase_info *done)
|
|
||||||
{
|
|
||||||
wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
|
|
||||||
wake_up(wait_q);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
|
static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = cxt->mtd;
|
struct mtd_info *mtd = cxt->mtd;
|
||||||
|
@ -97,34 +91,20 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
|
||||||
u32 start_page = start_page_offset / record_size;
|
u32 start_page = start_page_offset / record_size;
|
||||||
u32 erase_pages = mtd->erasesize / record_size;
|
u32 erase_pages = mtd->erasesize / record_size;
|
||||||
struct erase_info erase;
|
struct erase_info erase;
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
|
||||||
wait_queue_head_t wait_q;
|
|
||||||
int ret;
|
int ret;
|
||||||
int page;
|
int page;
|
||||||
|
|
||||||
init_waitqueue_head(&wait_q);
|
|
||||||
erase.mtd = mtd;
|
|
||||||
erase.callback = mtdoops_erase_callback;
|
|
||||||
erase.addr = offset;
|
erase.addr = offset;
|
||||||
erase.len = mtd->erasesize;
|
erase.len = mtd->erasesize;
|
||||||
erase.priv = (u_long)&wait_q;
|
|
||||||
|
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
add_wait_queue(&wait_q, &wait);
|
|
||||||
|
|
||||||
ret = mtd_erase(mtd, &erase);
|
ret = mtd_erase(mtd, &erase);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
set_current_state(TASK_RUNNING);
|
|
||||||
remove_wait_queue(&wait_q, &wait);
|
|
||||||
printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
|
printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
|
||||||
(unsigned long long)erase.addr,
|
(unsigned long long)erase.addr,
|
||||||
(unsigned long long)erase.len, mtddev);
|
(unsigned long long)erase.len, mtddev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule(); /* Wait for erase to finish. */
|
|
||||||
remove_wait_queue(&wait_q, &wait);
|
|
||||||
|
|
||||||
/* Mark pages as unused */
|
/* Mark pages as unused */
|
||||||
for (page = start_page; page < start_page + erase_pages; page++)
|
for (page = start_page; page < start_page + erase_pages; page++)
|
||||||
mark_page_unused(cxt, page);
|
mark_page_unused(cxt, page);
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
#include "mtdcore.h"
|
#include "mtdcore.h"
|
||||||
|
|
||||||
|
@ -205,28 +206,13 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
|
|
||||||
instr->addr += part->offset;
|
instr->addr += part->offset;
|
||||||
ret = part->parent->_erase(part->parent, instr);
|
ret = part->parent->_erase(part->parent, instr);
|
||||||
if (ret) {
|
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
|
||||||
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
|
instr->fail_addr -= part->offset;
|
||||||
instr->fail_addr -= part->offset;
|
instr->addr -= part->offset;
|
||||||
instr->addr -= part->offset;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtd_erase_callback(struct erase_info *instr)
|
|
||||||
{
|
|
||||||
if (instr->mtd->_erase == part_erase) {
|
|
||||||
struct mtd_part *part = mtd_to_part(instr->mtd);
|
|
||||||
|
|
||||||
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
|
|
||||||
instr->fail_addr -= part->offset;
|
|
||||||
instr->addr -= part->offset;
|
|
||||||
}
|
|
||||||
if (instr->callback)
|
|
||||||
instr->callback(instr);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(mtd_erase_callback);
|
|
||||||
|
|
||||||
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||||
{
|
{
|
||||||
struct mtd_part *part = mtd_to_part(mtd);
|
struct mtd_part *part = mtd_to_part(mtd);
|
||||||
|
@ -860,6 +846,92 @@ static int mtd_part_do_parse(struct mtd_part_parser *parser,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mtd_part_get_compatible_parser - find MTD parser by a compatible string
|
||||||
|
*
|
||||||
|
* @compat: compatible string describing partitions in a device tree
|
||||||
|
*
|
||||||
|
* MTD parsers can specify supported partitions by providing a table of
|
||||||
|
* compatibility strings. This function finds a parser that advertises support
|
||||||
|
* for a passed value of "compatible".
|
||||||
|
*/
|
||||||
|
static struct mtd_part_parser *mtd_part_get_compatible_parser(const char *compat)
|
||||||
|
{
|
||||||
|
struct mtd_part_parser *p, *ret = NULL;
|
||||||
|
|
||||||
|
spin_lock(&part_parser_lock);
|
||||||
|
|
||||||
|
list_for_each_entry(p, &part_parsers, list) {
|
||||||
|
const struct of_device_id *matches;
|
||||||
|
|
||||||
|
matches = p->of_match_table;
|
||||||
|
if (!matches)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (; matches->compatible[0]; matches++) {
|
||||||
|
if (!strcmp(matches->compatible, compat) &&
|
||||||
|
try_module_get(p->owner)) {
|
||||||
|
ret = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock(&part_parser_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mtd_part_of_parse(struct mtd_info *master,
|
||||||
|
struct mtd_partitions *pparts)
|
||||||
|
{
|
||||||
|
struct mtd_part_parser *parser;
|
||||||
|
struct device_node *np;
|
||||||
|
struct property *prop;
|
||||||
|
const char *compat;
|
||||||
|
const char *fixed = "fixed-partitions";
|
||||||
|
int ret, err = 0;
|
||||||
|
|
||||||
|
np = of_get_child_by_name(mtd_get_of_node(master), "partitions");
|
||||||
|
of_property_for_each_string(np, "compatible", prop, compat) {
|
||||||
|
parser = mtd_part_get_compatible_parser(compat);
|
||||||
|
if (!parser)
|
||||||
|
continue;
|
||||||
|
ret = mtd_part_do_parse(parser, master, pparts, NULL);
|
||||||
|
if (ret > 0) {
|
||||||
|
of_node_put(np);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
mtd_part_parser_put(parser);
|
||||||
|
if (ret < 0 && !err)
|
||||||
|
err = ret;
|
||||||
|
}
|
||||||
|
of_node_put(np);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For backward compatibility we have to try the "fixed-partitions"
|
||||||
|
* parser. It supports old DT format with partitions specified as a
|
||||||
|
* direct subnodes of a flash device DT node without any compatibility
|
||||||
|
* specified we could match.
|
||||||
|
*/
|
||||||
|
parser = mtd_part_parser_get(fixed);
|
||||||
|
if (!parser && !request_module("%s", fixed))
|
||||||
|
parser = mtd_part_parser_get(fixed);
|
||||||
|
if (parser) {
|
||||||
|
ret = mtd_part_do_parse(parser, master, pparts, NULL);
|
||||||
|
if (ret > 0)
|
||||||
|
return ret;
|
||||||
|
mtd_part_parser_put(parser);
|
||||||
|
if (ret < 0 && !err)
|
||||||
|
err = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* parse_mtd_partitions - parse MTD partitions
|
* parse_mtd_partitions - parse MTD partitions
|
||||||
* @master: the master partition (describes whole MTD device)
|
* @master: the master partition (describes whole MTD device)
|
||||||
|
@ -892,19 +964,30 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
|
||||||
types = default_mtd_part_types;
|
types = default_mtd_part_types;
|
||||||
|
|
||||||
for ( ; *types; types++) {
|
for ( ; *types; types++) {
|
||||||
pr_debug("%s: parsing partitions %s\n", master->name, *types);
|
/*
|
||||||
parser = mtd_part_parser_get(*types);
|
* ofpart is a special type that means OF partitioning info
|
||||||
if (!parser && !request_module("%s", *types))
|
* should be used. It requires a bit different logic so it is
|
||||||
|
* handled in a separated function.
|
||||||
|
*/
|
||||||
|
if (!strcmp(*types, "ofpart")) {
|
||||||
|
ret = mtd_part_of_parse(master, pparts);
|
||||||
|
} else {
|
||||||
|
pr_debug("%s: parsing partitions %s\n", master->name,
|
||||||
|
*types);
|
||||||
parser = mtd_part_parser_get(*types);
|
parser = mtd_part_parser_get(*types);
|
||||||
pr_debug("%s: got parser %s\n", master->name,
|
if (!parser && !request_module("%s", *types))
|
||||||
parser ? parser->name : NULL);
|
parser = mtd_part_parser_get(*types);
|
||||||
if (!parser)
|
pr_debug("%s: got parser %s\n", master->name,
|
||||||
continue;
|
parser ? parser->name : NULL);
|
||||||
ret = mtd_part_do_parse(parser, master, pparts, data);
|
if (!parser)
|
||||||
|
continue;
|
||||||
|
ret = mtd_part_do_parse(parser, master, pparts, data);
|
||||||
|
if (ret <= 0)
|
||||||
|
mtd_part_parser_put(parser);
|
||||||
|
}
|
||||||
/* Found partitions! */
|
/* Found partitions! */
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
return 0;
|
return 0;
|
||||||
mtd_part_parser_put(parser);
|
|
||||||
/*
|
/*
|
||||||
* Stash the first error we see; only report it if no parser
|
* Stash the first error we see; only report it if no parser
|
||||||
* succeeds
|
* succeeds
|
||||||
|
|
|
@ -536,18 +536,10 @@ static void mtdswap_store_eb(struct mtdswap_dev *d, struct swap_eb *eb)
|
||||||
mtdswap_rb_add(d, eb, MTDSWAP_HIFRAG);
|
mtdswap_rb_add(d, eb, MTDSWAP_HIFRAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void mtdswap_erase_callback(struct erase_info *done)
|
|
||||||
{
|
|
||||||
wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
|
|
||||||
wake_up(wait_q);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb)
|
static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = d->mtd;
|
struct mtd_info *mtd = d->mtd;
|
||||||
struct erase_info erase;
|
struct erase_info erase;
|
||||||
wait_queue_head_t wq;
|
|
||||||
unsigned int retries = 0;
|
unsigned int retries = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -556,14 +548,9 @@ static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb)
|
||||||
d->max_erase_count = eb->erase_count;
|
d->max_erase_count = eb->erase_count;
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
init_waitqueue_head(&wq);
|
|
||||||
memset(&erase, 0, sizeof(struct erase_info));
|
memset(&erase, 0, sizeof(struct erase_info));
|
||||||
|
|
||||||
erase.mtd = mtd;
|
|
||||||
erase.callback = mtdswap_erase_callback;
|
|
||||||
erase.addr = mtdswap_eb_offset(d, eb);
|
erase.addr = mtdswap_eb_offset(d, eb);
|
||||||
erase.len = mtd->erasesize;
|
erase.len = mtd->erasesize;
|
||||||
erase.priv = (u_long)&wq;
|
|
||||||
|
|
||||||
ret = mtd_erase(mtd, &erase);
|
ret = mtd_erase(mtd, &erase);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -582,27 +569,6 @@ retry:
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = wait_event_interruptible(wq, erase.state == MTD_ERASE_DONE ||
|
|
||||||
erase.state == MTD_ERASE_FAILED);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(d->dev, "Interrupted erase block %#llx erasure on %s\n",
|
|
||||||
erase.addr, mtd->name);
|
|
||||||
return -EINTR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (erase.state == MTD_ERASE_FAILED) {
|
|
||||||
if (retries++ < MTDSWAP_ERASE_RETRIES) {
|
|
||||||
dev_warn(d->dev,
|
|
||||||
"erase of erase block %#llx on %s failed",
|
|
||||||
erase.addr, mtd->name);
|
|
||||||
yield();
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
|
|
||||||
mtdswap_handle_badblock(d, eb);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,569 +1,6 @@
|
||||||
config MTD_NAND_ECC
|
config MTD_NAND_CORE
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
config MTD_NAND_ECC_SMC
|
source "drivers/mtd/nand/onenand/Kconfig"
|
||||||
bool "NAND ECC Smart Media byte order"
|
|
||||||
depends on MTD_NAND_ECC
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Software ECC according to the Smart Media Specification.
|
|
||||||
The original Linux implementation had byte 0 and 1 swapped.
|
|
||||||
|
|
||||||
|
source "drivers/mtd/nand/raw/Kconfig"
|
||||||
menuconfig MTD_NAND
|
|
||||||
tristate "NAND Device Support"
|
|
||||||
depends on MTD
|
|
||||||
select MTD_NAND_ECC
|
|
||||||
help
|
|
||||||
This enables support for accessing all type of NAND flash
|
|
||||||
devices. For further information see
|
|
||||||
<http://www.linux-mtd.infradead.org/doc/nand.html>.
|
|
||||||
|
|
||||||
if MTD_NAND
|
|
||||||
|
|
||||||
config MTD_NAND_BCH
|
|
||||||
tristate
|
|
||||||
select BCH
|
|
||||||
depends on MTD_NAND_ECC_BCH
|
|
||||||
default MTD_NAND
|
|
||||||
|
|
||||||
config MTD_NAND_ECC_BCH
|
|
||||||
bool "Support software BCH ECC"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
This enables support for software BCH error correction. Binary BCH
|
|
||||||
codes are more powerful and cpu intensive than traditional Hamming
|
|
||||||
ECC codes. They are used with NAND devices requiring more than 1 bit
|
|
||||||
of error correction.
|
|
||||||
|
|
||||||
config MTD_SM_COMMON
|
|
||||||
tristate
|
|
||||||
default n
|
|
||||||
|
|
||||||
config MTD_NAND_DENALI
|
|
||||||
tristate
|
|
||||||
|
|
||||||
config MTD_NAND_DENALI_PCI
|
|
||||||
tristate "Support Denali NAND controller on Intel Moorestown"
|
|
||||||
select MTD_NAND_DENALI
|
|
||||||
depends on HAS_DMA && PCI
|
|
||||||
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"
|
|
||||||
select MTD_NAND_DENALI
|
|
||||||
depends on HAS_DMA && HAVE_CLK && OF
|
|
||||||
help
|
|
||||||
Enable the driver for NAND flash on platforms using a Denali NAND
|
|
||||||
controller as a DT device.
|
|
||||||
|
|
||||||
config MTD_NAND_GPIO
|
|
||||||
tristate "GPIO assisted NAND Flash driver"
|
|
||||||
depends on GPIOLIB || COMPILE_TEST
|
|
||||||
depends on HAS_IOMEM
|
|
||||||
help
|
|
||||||
This enables a NAND flash driver where control signals are
|
|
||||||
connected to GPIO pins, and commands and data are communicated
|
|
||||||
via a memory mapped interface.
|
|
||||||
|
|
||||||
config MTD_NAND_AMS_DELTA
|
|
||||||
tristate "NAND Flash device on Amstrad E3"
|
|
||||||
depends on MACH_AMS_DELTA
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
Support for NAND flash on Amstrad E3 (Delta).
|
|
||||||
|
|
||||||
config MTD_NAND_OMAP2
|
|
||||||
tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
|
|
||||||
depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
|
|
||||||
help
|
|
||||||
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
|
|
||||||
and Keystone platforms.
|
|
||||||
|
|
||||||
config MTD_NAND_OMAP_BCH
|
|
||||||
depends on MTD_NAND_OMAP2
|
|
||||||
bool "Support hardware based BCH error correction"
|
|
||||||
default n
|
|
||||||
select BCH
|
|
||||||
help
|
|
||||||
This config enables the ELM hardware engine, which can be used to
|
|
||||||
locate and correct errors when using BCH ECC scheme. This offloads
|
|
||||||
the cpu from doing ECC error searching and correction. However some
|
|
||||||
legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
|
|
||||||
so this is optional for them.
|
|
||||||
|
|
||||||
config MTD_NAND_OMAP_BCH_BUILD
|
|
||||||
def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH
|
|
||||||
|
|
||||||
config MTD_NAND_RICOH
|
|
||||||
tristate "Ricoh xD card reader"
|
|
||||||
default n
|
|
||||||
depends on PCI
|
|
||||||
select MTD_SM_COMMON
|
|
||||||
help
|
|
||||||
Enable support for Ricoh R5C852 xD card reader
|
|
||||||
You also need to enable ether
|
|
||||||
NAND SSFDC (SmartMedia) read only translation layer' or new
|
|
||||||
expermental, readwrite
|
|
||||||
'SmartMedia/xD new translation layer'
|
|
||||||
|
|
||||||
config MTD_NAND_AU1550
|
|
||||||
tristate "Au1550/1200 NAND support"
|
|
||||||
depends on MIPS_ALCHEMY
|
|
||||||
help
|
|
||||||
This enables the driver for the NAND flash controller on the
|
|
||||||
AMD/Alchemy 1550 SOC.
|
|
||||||
|
|
||||||
config MTD_NAND_BF5XX
|
|
||||||
tristate "Blackfin on-chip NAND Flash Controller driver"
|
|
||||||
depends on BF54x || BF52x
|
|
||||||
help
|
|
||||||
This enables the Blackfin on-chip NAND flash controller
|
|
||||||
|
|
||||||
No board specific support is done by this driver, each board
|
|
||||||
must advertise a platform_device for the driver to attach.
|
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
|
||||||
will be called bf5xx-nand.
|
|
||||||
|
|
||||||
config MTD_NAND_BF5XX_HWECC
|
|
||||||
bool "BF5XX NAND Hardware ECC"
|
|
||||||
default y
|
|
||||||
depends on MTD_NAND_BF5XX
|
|
||||||
help
|
|
||||||
Enable the use of the BF5XX's internal ECC generator when
|
|
||||||
using NAND.
|
|
||||||
|
|
||||||
config MTD_NAND_BF5XX_BOOTROM_ECC
|
|
||||||
bool "Use Blackfin BootROM ECC Layout"
|
|
||||||
default n
|
|
||||||
depends on MTD_NAND_BF5XX_HWECC
|
|
||||||
help
|
|
||||||
If you wish to modify NAND pages and allow the Blackfin on-chip
|
|
||||||
BootROM to boot from them, say Y here. This is only necessary
|
|
||||||
if you are booting U-Boot out of NAND and you wish to update
|
|
||||||
U-Boot from Linux' userspace. Otherwise, you should say N here.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config MTD_NAND_S3C2410
|
|
||||||
tristate "NAND Flash support for Samsung S3C SoCs"
|
|
||||||
depends on ARCH_S3C24XX || ARCH_S3C64XX
|
|
||||||
help
|
|
||||||
This enables the NAND flash controller on the S3C24xx and S3C64xx
|
|
||||||
SoCs
|
|
||||||
|
|
||||||
No board specific support is done by this driver, each board
|
|
||||||
must advertise a platform_device for the driver to attach.
|
|
||||||
|
|
||||||
config MTD_NAND_S3C2410_DEBUG
|
|
||||||
bool "Samsung S3C NAND driver debug"
|
|
||||||
depends on MTD_NAND_S3C2410
|
|
||||||
help
|
|
||||||
Enable debugging of the S3C NAND driver
|
|
||||||
|
|
||||||
config MTD_NAND_NDFC
|
|
||||||
tristate "NDFC NanD Flash Controller"
|
|
||||||
depends on 4xx
|
|
||||||
select MTD_NAND_ECC_SMC
|
|
||||||
help
|
|
||||||
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
|
||||||
|
|
||||||
config MTD_NAND_S3C2410_CLKSTOP
|
|
||||||
bool "Samsung S3C NAND IDLE clock stop"
|
|
||||||
depends on MTD_NAND_S3C2410
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Stop the clock to the NAND controller when there is no chip
|
|
||||||
selected to save power. This will mean there is a small delay
|
|
||||||
when the is NAND chip selected or released, but will save
|
|
||||||
approximately 5mA of power when there is nothing happening.
|
|
||||||
|
|
||||||
config MTD_NAND_TANGO
|
|
||||||
tristate "NAND Flash support for Tango chips"
|
|
||||||
depends on ARCH_TANGO || COMPILE_TEST
|
|
||||||
depends on HAS_DMA
|
|
||||||
help
|
|
||||||
Enables the NAND Flash controller on Tango chips.
|
|
||||||
|
|
||||||
config MTD_NAND_DISKONCHIP
|
|
||||||
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
|
|
||||||
depends on HAS_IOMEM
|
|
||||||
select REED_SOLOMON
|
|
||||||
select REED_SOLOMON_DEC16
|
|
||||||
help
|
|
||||||
This is a reimplementation of M-Systems DiskOnChip 2000,
|
|
||||||
Millennium and Millennium Plus as a standard NAND device driver,
|
|
||||||
as opposed to the earlier self-contained MTD device drivers.
|
|
||||||
This should enable, among other things, proper JFFS2 operation on
|
|
||||||
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'.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
(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.
|
|
||||||
|
|
||||||
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'.
|
|
||||||
|
|
||||||
config MTD_NAND_DISKONCHIP_BBTWRITE
|
|
||||||
bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
|
|
||||||
depends on MTD_NAND_DISKONCHIP
|
|
||||||
help
|
|
||||||
On DiskOnChip devices shipped with the INFTL filesystem (Millennium
|
|
||||||
and 2000 TSOP/Alon), Linux reserves some space at the end of the
|
|
||||||
device for the Bad Block Table (BBT). If you have existing INFTL
|
|
||||||
data on your device (created by non-Linux tools such as M-Systems'
|
|
||||||
DOS drivers), your data might overlap the area Linux wants to use for
|
|
||||||
the BBT. If this is a concern for you, leave this option disabled and
|
|
||||||
Linux will not write BBT data into this area.
|
|
||||||
The downside of leaving this option disabled is that if bad blocks
|
|
||||||
are detected by Linux, they will not be recorded in the BBT, which
|
|
||||||
could cause future problems.
|
|
||||||
Once you enable this option, new filesystems (INFTL or others, created
|
|
||||||
in Linux or other operating systems) will not use the reserved area.
|
|
||||||
The only reason not to enable this option is to prevent damage to
|
|
||||||
preexisting filesystems.
|
|
||||||
Even if you leave this disabled, you can enable BBT writes at module
|
|
||||||
load time (assuming you build diskonchip as a module) with the module
|
|
||||||
parameter "inftl_bbt_write=1".
|
|
||||||
|
|
||||||
config MTD_NAND_DOCG4
|
|
||||||
tristate "Support for DiskOnChip G4"
|
|
||||||
depends on HAS_IOMEM
|
|
||||||
select BCH
|
|
||||||
select BITREVERSE
|
|
||||||
help
|
|
||||||
Support for diskonchip G4 nand flash, found in various smartphones and
|
|
||||||
PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba
|
|
||||||
Portege G900, Asus P526, and O2 XDA Zinc.
|
|
||||||
|
|
||||||
With this driver you will be able to use UBI and create a ubifs on the
|
|
||||||
device, so you may wish to consider enabling UBI and UBIFS as well.
|
|
||||||
|
|
||||||
These devices ship with the Mys/Sandisk SAFTL formatting, for which
|
|
||||||
there is currently no mtd parser, so you may want to use command line
|
|
||||||
partitioning to segregate write-protected blocks. On the Treo680, the
|
|
||||||
first five erase blocks (256KiB each) are write-protected, followed
|
|
||||||
by the block containing the saftl partition table. This is probably
|
|
||||||
typical.
|
|
||||||
|
|
||||||
config MTD_NAND_SHARPSL
|
|
||||||
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
|
|
||||||
depends on ARCH_PXA
|
|
||||||
|
|
||||||
config MTD_NAND_CAFE
|
|
||||||
tristate "NAND support for OLPC CAFÉ chip"
|
|
||||||
depends on PCI
|
|
||||||
select REED_SOLOMON
|
|
||||||
select REED_SOLOMON_DEC16
|
|
||||||
help
|
|
||||||
Use NAND flash attached to the CAFÉ chip designed for the OLPC
|
|
||||||
laptop.
|
|
||||||
|
|
||||||
config MTD_NAND_CS553X
|
|
||||||
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
|
|
||||||
depends on X86_32
|
|
||||||
depends on !UML && HAS_IOMEM
|
|
||||||
help
|
|
||||||
The CS553x companion chips for the AMD Geode processor
|
|
||||||
include NAND flash controllers with built-in hardware ECC
|
|
||||||
capabilities; enabling this option will allow you to use
|
|
||||||
these. The driver will check the MSRs to verify that the
|
|
||||||
controller is enabled for NAND, and currently requires that
|
|
||||||
the controller be in MMIO mode.
|
|
||||||
|
|
||||||
If you say "m", the module will be called cs553x_nand.
|
|
||||||
|
|
||||||
config MTD_NAND_ATMEL
|
|
||||||
tristate "Support for NAND Flash / SmartMedia on AT91"
|
|
||||||
depends on ARCH_AT91
|
|
||||||
select MFD_ATMEL_SMC
|
|
||||||
help
|
|
||||||
Enables support for NAND Flash / Smart Media Card interface
|
|
||||||
on Atmel AT91 processors.
|
|
||||||
|
|
||||||
config MTD_NAND_MARVELL
|
|
||||||
tristate "NAND controller support on Marvell boards"
|
|
||||||
depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
|
|
||||||
COMPILE_TEST
|
|
||||||
depends on HAS_IOMEM && HAS_DMA
|
|
||||||
help
|
|
||||||
This enables the NAND flash controller driver for Marvell boards,
|
|
||||||
including:
|
|
||||||
- PXA3xx processors (NFCv1)
|
|
||||||
- 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
|
|
||||||
- 64-bit Aramda platforms (7k, 8k) (NFCv2)
|
|
||||||
|
|
||||||
config MTD_NAND_SLC_LPC32XX
|
|
||||||
tristate "NXP LPC32xx SLC Controller"
|
|
||||||
depends on ARCH_LPC32XX
|
|
||||||
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
|
|
||||||
reference board which contains a NAND256R3A2CZA6 chip.
|
|
||||||
|
|
||||||
Please check the actual NAND chip connected and its support
|
|
||||||
by the SLC NAND controller.
|
|
||||||
|
|
||||||
config MTD_NAND_MLC_LPC32XX
|
|
||||||
tristate "NXP LPC32xx MLC Controller"
|
|
||||||
depends on ARCH_LPC32XX
|
|
||||||
help
|
|
||||||
Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
|
|
||||||
controller. This is the default for the WORK92105 controller
|
|
||||||
board.
|
|
||||||
|
|
||||||
Please check the actual NAND chip connected and its support
|
|
||||||
by the MLC NAND controller.
|
|
||||||
|
|
||||||
config MTD_NAND_CM_X270
|
|
||||||
tristate "Support for NAND Flash on CM-X270 modules"
|
|
||||||
depends on MACH_ARMCORE
|
|
||||||
|
|
||||||
config MTD_NAND_PASEMI
|
|
||||||
tristate "NAND support for PA Semi PWRficient"
|
|
||||||
depends on PPC_PASEMI
|
|
||||||
help
|
|
||||||
Enables support for NAND Flash interface on PA Semi PWRficient
|
|
||||||
based boards
|
|
||||||
|
|
||||||
config MTD_NAND_TMIO
|
|
||||||
tristate "NAND Flash device on Toshiba Mobile IO Controller"
|
|
||||||
depends on MFD_TMIO
|
|
||||||
help
|
|
||||||
Support for NAND flash connected to a Toshiba Mobile IO
|
|
||||||
Controller in some PDAs, including the Sharp SL6000x.
|
|
||||||
|
|
||||||
config MTD_NAND_NANDSIM
|
|
||||||
tristate "Support for NAND Flash Simulator"
|
|
||||||
help
|
|
||||||
The simulator may simulate various NAND flash chips for the
|
|
||||||
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.
|
|
||||||
|
|
||||||
config MTD_NAND_BRCMNAND
|
|
||||||
tristate "Broadcom STB NAND controller"
|
|
||||||
depends on ARM || ARM64 || MIPS
|
|
||||||
help
|
|
||||||
Enables the Broadcom NAND controller driver. The controller was
|
|
||||||
originally designed for Set-Top Box but is used on various BCM7xxx,
|
|
||||||
BCM3xxx, BCM63xxx, iProc/Cygnus and more.
|
|
||||||
|
|
||||||
config MTD_NAND_BCM47XXNFLASH
|
|
||||||
tristate "Support for NAND flash on BCM4706 BCMA bus"
|
|
||||||
depends on BCMA_NFLASH
|
|
||||||
help
|
|
||||||
BCMA bus can have various flash memories attached, they are
|
|
||||||
registered by bcma as platform devices. This enables driver for
|
|
||||||
NAND flash memories. For now only BCM4706 is supported.
|
|
||||||
|
|
||||||
config MTD_NAND_PLATFORM
|
|
||||||
tristate "Support for generic platform NAND driver"
|
|
||||||
depends on HAS_IOMEM
|
|
||||||
help
|
|
||||||
This implements a generic NAND driver for on-SOC platform
|
|
||||||
devices. You will need to provide platform-specific functions
|
|
||||||
via platform_data.
|
|
||||||
|
|
||||||
config MTD_NAND_ORION
|
|
||||||
tristate "NAND Flash support for Marvell Orion SoC"
|
|
||||||
depends on PLAT_ORION
|
|
||||||
help
|
|
||||||
This enables the NAND flash controller on Orion machines.
|
|
||||||
|
|
||||||
No board specific support is done by this driver, each board
|
|
||||||
must advertise a platform_device for the driver to attach.
|
|
||||||
|
|
||||||
config MTD_NAND_OXNAS
|
|
||||||
tristate "NAND Flash support for Oxford Semiconductor SoC"
|
|
||||||
depends on ARCH_OXNAS || COMPILE_TEST
|
|
||||||
depends on HAS_IOMEM
|
|
||||||
help
|
|
||||||
This enables the NAND flash controller on Oxford Semiconductor SoCs.
|
|
||||||
|
|
||||||
config MTD_NAND_FSL_ELBC
|
|
||||||
tristate "NAND support for Freescale eLBC controllers"
|
|
||||||
depends on FSL_SOC
|
|
||||||
select FSL_LBC
|
|
||||||
help
|
|
||||||
Various Freescale chips, including the 8313, include a NAND Flash
|
|
||||||
Controller Module with built-in hardware ECC capabilities.
|
|
||||||
Enabling this option will enable you to use this to control
|
|
||||||
external NAND devices.
|
|
||||||
|
|
||||||
config MTD_NAND_FSL_IFC
|
|
||||||
tristate "NAND support for Freescale IFC controller"
|
|
||||||
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
|
|
||||||
select FSL_IFC
|
|
||||||
select MEMORY
|
|
||||||
help
|
|
||||||
Various Freescale chips e.g P1010, include a NAND Flash machine
|
|
||||||
with built-in hardware ECC capabilities.
|
|
||||||
Enabling this option will enable you to use this to control
|
|
||||||
external NAND devices.
|
|
||||||
|
|
||||||
config MTD_NAND_FSL_UPM
|
|
||||||
tristate "Support for NAND on Freescale UPM"
|
|
||||||
depends on PPC_83xx || PPC_85xx
|
|
||||||
select FSL_LBC
|
|
||||||
help
|
|
||||||
Enables support for NAND Flash chips wired onto Freescale PowerPC
|
|
||||||
processor localbus with User-Programmable Machine support.
|
|
||||||
|
|
||||||
config MTD_NAND_MPC5121_NFC
|
|
||||||
tristate "MPC5121 built-in NAND Flash Controller support"
|
|
||||||
depends on PPC_MPC512x
|
|
||||||
help
|
|
||||||
This enables the driver for the NAND flash controller on the
|
|
||||||
MPC5121 SoC.
|
|
||||||
|
|
||||||
config MTD_NAND_VF610_NFC
|
|
||||||
tristate "Support for Freescale NFC for VF610/MPC5125"
|
|
||||||
depends on (SOC_VF610 || COMPILE_TEST)
|
|
||||||
depends on HAS_IOMEM
|
|
||||||
help
|
|
||||||
Enables support for NAND Flash Controller on some Freescale
|
|
||||||
processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
|
|
||||||
The driver supports a maximum 2k page size. With 2k pages and
|
|
||||||
64 bytes or more of OOB, hardware ECC with up to 32-bit error
|
|
||||||
correction is supported. Hardware ECC is only enabled through
|
|
||||||
device tree.
|
|
||||||
|
|
||||||
config MTD_NAND_MXC
|
|
||||||
tristate "MXC NAND support"
|
|
||||||
depends on ARCH_MXC
|
|
||||||
help
|
|
||||||
This enables the driver for the NAND flash controller on the
|
|
||||||
MXC processors.
|
|
||||||
|
|
||||||
config MTD_NAND_SH_FLCTL
|
|
||||||
tristate "Support for NAND on Renesas SuperH FLCTL"
|
|
||||||
depends on SUPERH || COMPILE_TEST
|
|
||||||
depends on HAS_IOMEM
|
|
||||||
depends on HAS_DMA
|
|
||||||
help
|
|
||||||
Several Renesas SuperH CPU has FLCTL. This option enables support
|
|
||||||
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
|
|
||||||
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
|
|
||||||
help
|
|
||||||
This enables the NAND flash controller on the TXx9 SoCs.
|
|
||||||
|
|
||||||
config MTD_NAND_SOCRATES
|
|
||||||
tristate "Support for NAND on Socrates board"
|
|
||||||
depends on SOCRATES
|
|
||||||
help
|
|
||||||
Enables support for NAND Flash chips wired onto Socrates board.
|
|
||||||
|
|
||||||
config MTD_NAND_NUC900
|
|
||||||
tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
|
|
||||||
depends on ARCH_W90X900
|
|
||||||
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
|
|
||||||
help
|
|
||||||
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
|
|
||||||
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
|
|
||||||
help
|
|
||||||
Enables support for NAND Flash chips on the ST Microelectronics
|
|
||||||
Flexible Static Memory Controller (FSMC)
|
|
||||||
|
|
||||||
config MTD_NAND_XWAY
|
|
||||||
bool "Support for NAND on Lantiq XWAY SoC"
|
|
||||||
depends on LANTIQ && SOC_TYPE_XWAY
|
|
||||||
help
|
|
||||||
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
|
|
||||||
to the External Bus Unit (EBU).
|
|
||||||
|
|
||||||
config MTD_NAND_SUNXI
|
|
||||||
tristate "Support for NAND on Allwinner SoCs"
|
|
||||||
depends on ARCH_SUNXI
|
|
||||||
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_DMA
|
|
||||||
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
|
|
||||||
help
|
|
||||||
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
|
|
||||||
controller. This controller is found on IPQ806x SoC.
|
|
||||||
|
|
||||||
config MTD_NAND_MTK
|
|
||||||
tristate "Support for NAND controller on MTK SoCs"
|
|
||||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
|
||||||
depends on HAS_DMA
|
|
||||||
help
|
|
||||||
Enables support for NAND controller on MTK SoCs.
|
|
||||||
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
|
|
||||||
|
|
||||||
endif # MTD_NAND
|
|
||||||
|
|
|
@ -1,70 +1,7 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
#
|
|
||||||
# linux/drivers/nand/Makefile
|
|
||||||
#
|
|
||||||
|
|
||||||
obj-$(CONFIG_MTD_NAND) += nand.o
|
nandcore-objs := core.o bbt.o
|
||||||
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
|
obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
|
||||||
obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
|
|
||||||
obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
|
obj-y += onenand/
|
||||||
obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
|
obj-y += raw/
|
||||||
obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel/
|
|
||||||
obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
|
|
||||||
omap2_nand-objs := omap2.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
|
|
||||||
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
|
|
||||||
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
|
|
||||||
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
|
|
||||||
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
|
|
||||||
|
|
||||||
nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
|
|
||||||
nand-objs += nand_amd.o
|
|
||||||
nand-objs += nand_hynix.o
|
|
||||||
nand-objs += nand_macronix.o
|
|
||||||
nand-objs += nand_micron.o
|
|
||||||
nand-objs += nand_samsung.o
|
|
||||||
nand-objs += nand_toshiba.o
|
|
||||||
|
|
130
drivers/mtd/nand/bbt.c
Normal file
130
drivers/mtd/nand/bbt.c
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 Free Electrons
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||||
|
* Peter Pan <peterpandong@micron.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "nand-bbt: " fmt
|
||||||
|
|
||||||
|
#include <linux/mtd/nand.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_bbt_init() - Initialize the BBT (Bad Block Table)
|
||||||
|
* @nand: NAND device
|
||||||
|
*
|
||||||
|
* Initialize the in-memory BBT.
|
||||||
|
*
|
||||||
|
* Return: 0 in case of success, a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
int nanddev_bbt_init(struct nand_device *nand)
|
||||||
|
{
|
||||||
|
unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
|
||||||
|
unsigned int nblocks = nanddev_neraseblocks(nand);
|
||||||
|
unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
|
||||||
|
BITS_PER_LONG);
|
||||||
|
|
||||||
|
nand->bbt.cache = kzalloc(nwords, GFP_KERNEL);
|
||||||
|
if (!nand->bbt.cache)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_bbt_init);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table)
|
||||||
|
* @nand: NAND device
|
||||||
|
*
|
||||||
|
* Undoes what has been done in nanddev_bbt_init()
|
||||||
|
*/
|
||||||
|
void nanddev_bbt_cleanup(struct nand_device *nand)
|
||||||
|
{
|
||||||
|
kfree(nand->bbt.cache);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_bbt_update() - Update a BBT
|
||||||
|
* @nand: nand device
|
||||||
|
*
|
||||||
|
* Update the BBT. Currently a NOP function since on-flash bbt is not yet
|
||||||
|
* supported.
|
||||||
|
*
|
||||||
|
* Return: 0 in case of success, a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
int nanddev_bbt_update(struct nand_device *nand)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_bbt_update);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_bbt_get_block_status() - Return the status of an eraseblock
|
||||||
|
* @nand: nand device
|
||||||
|
* @entry: the BBT entry
|
||||||
|
*
|
||||||
|
* Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry
|
||||||
|
* is bigger than the BBT size.
|
||||||
|
*/
|
||||||
|
int nanddev_bbt_get_block_status(const struct nand_device *nand,
|
||||||
|
unsigned int entry)
|
||||||
|
{
|
||||||
|
unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
|
||||||
|
unsigned long *pos = nand->bbt.cache +
|
||||||
|
((entry * bits_per_block) / BITS_PER_LONG);
|
||||||
|
unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
|
||||||
|
unsigned long status;
|
||||||
|
|
||||||
|
if (entry >= nanddev_neraseblocks(nand))
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
status = pos[0] >> offs;
|
||||||
|
if (bits_per_block + offs > BITS_PER_LONG)
|
||||||
|
status |= pos[1] << (BITS_PER_LONG - offs);
|
||||||
|
|
||||||
|
return status & GENMASK(bits_per_block - 1, 0);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_bbt_set_block_status() - Update the status of an eraseblock in the
|
||||||
|
* in-memory BBT
|
||||||
|
* @nand: nand device
|
||||||
|
* @entry: the BBT entry to update
|
||||||
|
* @status: the new status
|
||||||
|
*
|
||||||
|
* Update an entry of the in-memory BBT. If you want to push the updated BBT
|
||||||
|
* the NAND you should call nanddev_bbt_update().
|
||||||
|
*
|
||||||
|
* Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT
|
||||||
|
* size.
|
||||||
|
*/
|
||||||
|
int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
|
||||||
|
enum nand_bbt_block_status status)
|
||||||
|
{
|
||||||
|
unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
|
||||||
|
unsigned long *pos = nand->bbt.cache +
|
||||||
|
((entry * bits_per_block) / BITS_PER_LONG);
|
||||||
|
unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG;
|
||||||
|
unsigned long val = status & GENMASK(bits_per_block - 1, 0);
|
||||||
|
|
||||||
|
if (entry >= nanddev_neraseblocks(nand))
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs);
|
||||||
|
pos[0] |= val << offs;
|
||||||
|
|
||||||
|
if (bits_per_block + offs > BITS_PER_LONG) {
|
||||||
|
unsigned int rbits = bits_per_block + offs - BITS_PER_LONG;
|
||||||
|
|
||||||
|
pos[1] &= ~GENMASK(rbits - 1, 0);
|
||||||
|
pos[1] |= val >> rbits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status);
|
|
@ -1,862 +0,0 @@
|
||||||
/* linux/drivers/mtd/nand/bf5xx_nand.c
|
|
||||||
*
|
|
||||||
* Copyright 2006-2008 Analog Devices Inc.
|
|
||||||
* http://blackfin.uclinux.org/
|
|
||||||
* Bryan Wu <bryan.wu@analog.com>
|
|
||||||
*
|
|
||||||
* Blackfin BF5xx on-chip NAND flash controller driver
|
|
||||||
*
|
|
||||||
* Derived from drivers/mtd/nand/s3c2410.c
|
|
||||||
* Copyright (c) 2007 Ben Dooks <ben@simtec.co.uk>
|
|
||||||
*
|
|
||||||
* Derived from drivers/mtd/nand/cafe.c
|
|
||||||
* Copyright © 2006 Red Hat, Inc.
|
|
||||||
* Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
|
|
||||||
*
|
|
||||||
* Changelog:
|
|
||||||
* 12-Jun-2007 Bryan Wu: Initial version
|
|
||||||
* 18-Jul-2007 Bryan Wu:
|
|
||||||
* - ECC_HW and ECC_SW supported
|
|
||||||
* - DMA supported in ECC_HW
|
|
||||||
* - YAFFS tested as rootfs in both ECC_HW and ECC_SW
|
|
||||||
*
|
|
||||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/dma-mapping.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/bitops.h>
|
|
||||||
|
|
||||||
#include <linux/mtd/mtd.h>
|
|
||||||
#include <linux/mtd/rawnand.h>
|
|
||||||
#include <linux/mtd/nand_ecc.h>
|
|
||||||
#include <linux/mtd/partitions.h>
|
|
||||||
|
|
||||||
#include <asm/blackfin.h>
|
|
||||||
#include <asm/dma.h>
|
|
||||||
#include <asm/cacheflush.h>
|
|
||||||
#include <asm/nand.h>
|
|
||||||
#include <asm/portmux.h>
|
|
||||||
|
|
||||||
#define DRV_NAME "bf5xx-nand"
|
|
||||||
#define DRV_VERSION "1.2"
|
|
||||||
#define DRV_AUTHOR "Bryan Wu <bryan.wu@analog.com>"
|
|
||||||
#define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver"
|
|
||||||
|
|
||||||
/* NFC_STAT Masks */
|
|
||||||
#define NBUSY 0x01 /* Not Busy */
|
|
||||||
#define WB_FULL 0x02 /* Write Buffer Full */
|
|
||||||
#define PG_WR_STAT 0x04 /* Page Write Pending */
|
|
||||||
#define PG_RD_STAT 0x08 /* Page Read Pending */
|
|
||||||
#define WB_EMPTY 0x10 /* Write Buffer Empty */
|
|
||||||
|
|
||||||
/* NFC_IRQSTAT Masks */
|
|
||||||
#define NBUSYIRQ 0x01 /* Not Busy IRQ */
|
|
||||||
#define WB_OVF 0x02 /* Write Buffer Overflow */
|
|
||||||
#define WB_EDGE 0x04 /* Write Buffer Edge Detect */
|
|
||||||
#define RD_RDY 0x08 /* Read Data Ready */
|
|
||||||
#define WR_DONE 0x10 /* Page Write Done */
|
|
||||||
|
|
||||||
/* NFC_RST Masks */
|
|
||||||
#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */
|
|
||||||
|
|
||||||
/* NFC_PGCTL Masks */
|
|
||||||
#define PG_RD_START 0x01 /* Page Read Start */
|
|
||||||
#define PG_WR_START 0x02 /* Page Write Start */
|
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_NAND_BF5XX_HWECC
|
|
||||||
static int hardware_ecc = 1;
|
|
||||||
#else
|
|
||||||
static int hardware_ecc;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const unsigned short bfin_nfc_pin_req[] =
|
|
||||||
{P_NAND_CE,
|
|
||||||
P_NAND_RB,
|
|
||||||
P_NAND_D0,
|
|
||||||
P_NAND_D1,
|
|
||||||
P_NAND_D2,
|
|
||||||
P_NAND_D3,
|
|
||||||
P_NAND_D4,
|
|
||||||
P_NAND_D5,
|
|
||||||
P_NAND_D6,
|
|
||||||
P_NAND_D7,
|
|
||||||
P_NAND_WE,
|
|
||||||
P_NAND_RE,
|
|
||||||
P_NAND_CLE,
|
|
||||||
P_NAND_ALE,
|
|
||||||
0};
|
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
|
|
||||||
static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section,
|
|
||||||
struct mtd_oob_region *oobregion)
|
|
||||||
{
|
|
||||||
if (section > 7)
|
|
||||||
return -ERANGE;
|
|
||||||
|
|
||||||
oobregion->offset = section * 8;
|
|
||||||
oobregion->length = 3;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bootrom_ooblayout_free(struct mtd_info *mtd, int section,
|
|
||||||
struct mtd_oob_region *oobregion)
|
|
||||||
{
|
|
||||||
if (section > 7)
|
|
||||||
return -ERANGE;
|
|
||||||
|
|
||||||
oobregion->offset = (section * 8) + 3;
|
|
||||||
oobregion->length = 5;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = {
|
|
||||||
.ecc = bootrom_ooblayout_ecc,
|
|
||||||
.free = bootrom_ooblayout_free,
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Data structures for bf5xx nand flash controller driver
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* bf5xx nand info */
|
|
||||||
struct bf5xx_nand_info {
|
|
||||||
/* mtd info */
|
|
||||||
struct nand_hw_control controller;
|
|
||||||
struct nand_chip chip;
|
|
||||||
|
|
||||||
/* platform info */
|
|
||||||
struct bf5xx_nand_platform *platform;
|
|
||||||
|
|
||||||
/* device info */
|
|
||||||
struct device *device;
|
|
||||||
|
|
||||||
/* DMA stuff */
|
|
||||||
struct completion dma_completion;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Conversion functions
|
|
||||||
*/
|
|
||||||
static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd)
|
|
||||||
{
|
|
||||||
return container_of(mtd_to_nand(mtd), struct bf5xx_nand_info,
|
|
||||||
chip);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return platform_get_drvdata(pdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return dev_get_platdata(&pdev->dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* struct nand_chip interface function pointers
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* bf5xx_nand_hwcontrol
|
|
||||||
*
|
|
||||||
* Issue command and address cycles to the chip
|
|
||||||
*/
|
|
||||||
static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd,
|
|
||||||
unsigned int ctrl)
|
|
||||||
{
|
|
||||||
if (cmd == NAND_CMD_NONE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (bfin_read_NFC_STAT() & WB_FULL)
|
|
||||||
cpu_relax();
|
|
||||||
|
|
||||||
if (ctrl & NAND_CLE)
|
|
||||||
bfin_write_NFC_CMD(cmd);
|
|
||||||
else if (ctrl & NAND_ALE)
|
|
||||||
bfin_write_NFC_ADDR(cmd);
|
|
||||||
SSYNC();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* bf5xx_nand_devready()
|
|
||||||
*
|
|
||||||
* returns 0 if the nand is busy, 1 if it is ready
|
|
||||||
*/
|
|
||||||
static int bf5xx_nand_devready(struct mtd_info *mtd)
|
|
||||||
{
|
|
||||||
unsigned short val = bfin_read_NFC_STAT();
|
|
||||||
|
|
||||||
if ((val & NBUSY) == NBUSY)
|
|
||||||
return 1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ECC functions
|
|
||||||
* These allow the bf5xx to use the controller's ECC
|
|
||||||
* generator block to ECC the data as it passes through
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ECC error correction function
|
|
||||||
*/
|
|
||||||
static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
|
|
||||||
u_char *read_ecc, u_char *calc_ecc)
|
|
||||||
{
|
|
||||||
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
|
|
||||||
u32 syndrome[5];
|
|
||||||
u32 calced, stored;
|
|
||||||
int i;
|
|
||||||
unsigned short failing_bit, failing_byte;
|
|
||||||
u_char data;
|
|
||||||
|
|
||||||
calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);
|
|
||||||
stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);
|
|
||||||
|
|
||||||
syndrome[0] = (calced ^ stored);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* syndrome 0: all zero
|
|
||||||
* No error in data
|
|
||||||
* No action
|
|
||||||
*/
|
|
||||||
if (!syndrome[0] || !calced || !stored)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* sysdrome 0: only one bit is one
|
|
||||||
* ECC data was incorrect
|
|
||||||
* No action
|
|
||||||
*/
|
|
||||||
if (hweight32(syndrome[0]) == 1) {
|
|
||||||
dev_err(info->device, "ECC data was incorrect!\n");
|
|
||||||
return -EBADMSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
|
|
||||||
syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);
|
|
||||||
syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);
|
|
||||||
syndrome[4] = syndrome[2] ^ syndrome[3];
|
|
||||||
|
|
||||||
for (i = 0; i < 5; i++)
|
|
||||||
dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]);
|
|
||||||
|
|
||||||
dev_info(info->device,
|
|
||||||
"calced[0x%08x], stored[0x%08x]\n",
|
|
||||||
calced, stored);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* sysdrome 0: exactly 11 bits are one, each parity
|
|
||||||
* and parity' pair is 1 & 0 or 0 & 1.
|
|
||||||
* 1-bit correctable error
|
|
||||||
* Correct the error
|
|
||||||
*/
|
|
||||||
if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {
|
|
||||||
dev_info(info->device,
|
|
||||||
"1-bit correctable error, correct it.\n");
|
|
||||||
dev_info(info->device,
|
|
||||||
"syndrome[1] 0x%08x\n", syndrome[1]);
|
|
||||||
|
|
||||||
failing_bit = syndrome[1] & 0x7;
|
|
||||||
failing_byte = syndrome[1] >> 0x3;
|
|
||||||
data = *(dat + failing_byte);
|
|
||||||
data = data ^ (0x1 << failing_bit);
|
|
||||||
*(dat + failing_byte) = data;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* sysdrome 0: random data
|
|
||||||
* More than 1-bit error, non-correctable error
|
|
||||||
* Discard data, mark bad block
|
|
||||||
*/
|
|
||||||
dev_err(info->device,
|
|
||||||
"More than 1-bit error, non-correctable error.\n");
|
|
||||||
dev_err(info->device,
|
|
||||||
"Please discard data, mark bad block\n");
|
|
||||||
|
|
||||||
return -EBADMSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
|
||||||
u_char *read_ecc, u_char *calc_ecc)
|
|
||||||
{
|
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
||||||
int ret, bitflips = 0;
|
|
||||||
|
|
||||||
ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
bitflips = ret;
|
|
||||||
|
|
||||||
/* If ecc size is 512, correct second 256 bytes */
|
|
||||||
if (chip->ecc.size == 512) {
|
|
||||||
dat += 256;
|
|
||||||
read_ecc += 3;
|
|
||||||
calc_ecc += 3;
|
|
||||||
ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
bitflips += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bitflips;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
|
|
||||||
const u_char *dat, u_char *ecc_code)
|
|
||||||
{
|
|
||||||
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
|
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
||||||
u16 ecc0, ecc1;
|
|
||||||
u32 code[2];
|
|
||||||
u8 *p;
|
|
||||||
|
|
||||||
/* first 3 bytes ECC code for 256 page size */
|
|
||||||
ecc0 = bfin_read_NFC_ECC0();
|
|
||||||
ecc1 = bfin_read_NFC_ECC1();
|
|
||||||
|
|
||||||
code[0] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);
|
|
||||||
|
|
||||||
dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]);
|
|
||||||
|
|
||||||
p = (u8 *) code;
|
|
||||||
memcpy(ecc_code, p, 3);
|
|
||||||
|
|
||||||
/* second 3 bytes ECC code for 512 ecc size */
|
|
||||||
if (chip->ecc.size == 512) {
|
|
||||||
ecc0 = bfin_read_NFC_ECC2();
|
|
||||||
ecc1 = bfin_read_NFC_ECC3();
|
|
||||||
code[1] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);
|
|
||||||
|
|
||||||
/* second 3 bytes in ecc_code for second 256
|
|
||||||
* bytes of 512 page size
|
|
||||||
*/
|
|
||||||
p = (u8 *) (code + 1);
|
|
||||||
memcpy((ecc_code + 3), p, 3);
|
|
||||||
dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* PIO mode for buffer writing and reading
|
|
||||||
*/
|
|
||||||
static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned short val;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Data reads are requested by first writing to NFC_DATA_RD
|
|
||||||
* and then reading back from NFC_READ.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
while (bfin_read_NFC_STAT() & WB_FULL)
|
|
||||||
cpu_relax();
|
|
||||||
|
|
||||||
/* Contents do not matter */
|
|
||||||
bfin_write_NFC_DATA_RD(0x0000);
|
|
||||||
SSYNC();
|
|
||||||
|
|
||||||
while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY)
|
|
||||||
cpu_relax();
|
|
||||||
|
|
||||||
buf[i] = bfin_read_NFC_READ();
|
|
||||||
|
|
||||||
val = bfin_read_NFC_IRQSTAT();
|
|
||||||
val |= RD_RDY;
|
|
||||||
bfin_write_NFC_IRQSTAT(val);
|
|
||||||
SSYNC();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd)
|
|
||||||
{
|
|
||||||
uint8_t val;
|
|
||||||
|
|
||||||
bf5xx_nand_read_buf(mtd, &val, 1);
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bf5xx_nand_write_buf(struct mtd_info *mtd,
|
|
||||||
const uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
while (bfin_read_NFC_STAT() & WB_FULL)
|
|
||||||
cpu_relax();
|
|
||||||
|
|
||||||
bfin_write_NFC_DATA_WR(buf[i]);
|
|
||||||
SSYNC();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
u16 *p = (u16 *) buf;
|
|
||||||
len >>= 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Data reads are requested by first writing to NFC_DATA_RD
|
|
||||||
* and then reading back from NFC_READ.
|
|
||||||
*/
|
|
||||||
bfin_write_NFC_DATA_RD(0x5555);
|
|
||||||
|
|
||||||
SSYNC();
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
p[i] = bfin_read_NFC_READ();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bf5xx_nand_write_buf16(struct mtd_info *mtd,
|
|
||||||
const uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
u16 *p = (u16 *) buf;
|
|
||||||
len >>= 1;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
bfin_write_NFC_DATA_WR(p[i]);
|
|
||||||
|
|
||||||
SSYNC();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* DMA functions for buffer writing and reading
|
|
||||||
*/
|
|
||||||
static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct bf5xx_nand_info *info = dev_id;
|
|
||||||
|
|
||||||
clear_dma_irqstat(CH_NFC);
|
|
||||||
disable_dma(CH_NFC);
|
|
||||||
complete(&info->dma_completion);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bf5xx_nand_dma_rw(struct mtd_info *mtd,
|
|
||||||
uint8_t *buf, int is_read)
|
|
||||||
{
|
|
||||||
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
|
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
||||||
unsigned short val;
|
|
||||||
|
|
||||||
dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n",
|
|
||||||
mtd, buf, is_read);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Before starting a dma transfer, be sure to invalidate/flush
|
|
||||||
* the cache over the address range of your DMA buffer to
|
|
||||||
* prevent cache coherency problems. Otherwise very subtle bugs
|
|
||||||
* can be introduced to your driver.
|
|
||||||
*/
|
|
||||||
if (is_read)
|
|
||||||
invalidate_dcache_range((unsigned int)buf,
|
|
||||||
(unsigned int)(buf + chip->ecc.size));
|
|
||||||
else
|
|
||||||
flush_dcache_range((unsigned int)buf,
|
|
||||||
(unsigned int)(buf + chip->ecc.size));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This register must be written before each page is
|
|
||||||
* transferred to generate the correct ECC register
|
|
||||||
* values.
|
|
||||||
*/
|
|
||||||
bfin_write_NFC_RST(ECC_RST);
|
|
||||||
SSYNC();
|
|
||||||
while (bfin_read_NFC_RST() & ECC_RST)
|
|
||||||
cpu_relax();
|
|
||||||
|
|
||||||
disable_dma(CH_NFC);
|
|
||||||
clear_dma_irqstat(CH_NFC);
|
|
||||||
|
|
||||||
/* setup DMA register with Blackfin DMA API */
|
|
||||||
set_dma_config(CH_NFC, 0x0);
|
|
||||||
set_dma_start_addr(CH_NFC, (unsigned long) buf);
|
|
||||||
|
|
||||||
/* The DMAs have different size on BF52x and BF54x */
|
|
||||||
#ifdef CONFIG_BF52x
|
|
||||||
set_dma_x_count(CH_NFC, (chip->ecc.size >> 1));
|
|
||||||
set_dma_x_modify(CH_NFC, 2);
|
|
||||||
val = DI_EN | WDSIZE_16;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_BF54x
|
|
||||||
set_dma_x_count(CH_NFC, (chip->ecc.size >> 2));
|
|
||||||
set_dma_x_modify(CH_NFC, 4);
|
|
||||||
val = DI_EN | WDSIZE_32;
|
|
||||||
#endif
|
|
||||||
/* setup write or read operation */
|
|
||||||
if (is_read)
|
|
||||||
val |= WNR;
|
|
||||||
set_dma_config(CH_NFC, val);
|
|
||||||
enable_dma(CH_NFC);
|
|
||||||
|
|
||||||
/* Start PAGE read/write operation */
|
|
||||||
if (is_read)
|
|
||||||
bfin_write_NFC_PGCTL(PG_RD_START);
|
|
||||||
else
|
|
||||||
bfin_write_NFC_PGCTL(PG_WR_START);
|
|
||||||
wait_for_completion(&info->dma_completion);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
|
|
||||||
uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
|
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
||||||
|
|
||||||
dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len);
|
|
||||||
|
|
||||||
if (len == chip->ecc.size)
|
|
||||||
bf5xx_nand_dma_rw(mtd, buf, 1);
|
|
||||||
else
|
|
||||||
bf5xx_nand_read_buf(mtd, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
|
|
||||||
const uint8_t *buf, int len)
|
|
||||||
{
|
|
||||||
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
|
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
||||||
|
|
||||||
dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len);
|
|
||||||
|
|
||||||
if (len == chip->ecc.size)
|
|
||||||
bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0);
|
|
||||||
else
|
|
||||||
bf5xx_nand_write_buf(mtd, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
||||||
uint8_t *buf, int oob_required, int page)
|
|
||||||
{
|
|
||||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
|
||||||
|
|
||||||
bf5xx_nand_read_buf(mtd, buf, mtd->writesize);
|
|
||||||
bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bf5xx_nand_write_page_raw(struct mtd_info *mtd,
|
|
||||||
struct nand_chip *chip, const uint8_t *buf, int oob_required,
|
|
||||||
int page)
|
|
||||||
{
|
|
||||||
nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
|
|
||||||
bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
||||||
|
|
||||||
return nand_prog_page_end_op(chip);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* System initialization functions
|
|
||||||
*/
|
|
||||||
static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Do not use dma */
|
|
||||||
if (!hardware_ecc)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
init_completion(&info->dma_completion);
|
|
||||||
|
|
||||||
/* Request NFC DMA channel */
|
|
||||||
ret = request_dma(CH_NFC, "BF5XX NFC driver");
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(info->device, " unable to get DMA channel\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_BF54x
|
|
||||||
/* Setup DMAC1 channel mux for NFC which shared with SDH */
|
|
||||||
bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1);
|
|
||||||
SSYNC();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info);
|
|
||||||
|
|
||||||
/* Turn off the DMA channel first */
|
|
||||||
disable_dma(CH_NFC);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bf5xx_nand_dma_remove(struct bf5xx_nand_info *info)
|
|
||||||
{
|
|
||||||
/* Free NFC DMA channel */
|
|
||||||
if (hardware_ecc)
|
|
||||||
free_dma(CH_NFC);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* BF5XX NFC hardware initialization
|
|
||||||
* - pin mux setup
|
|
||||||
* - clear interrupt status
|
|
||||||
*/
|
|
||||||
static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
unsigned short val;
|
|
||||||
struct bf5xx_nand_platform *plat = info->platform;
|
|
||||||
|
|
||||||
/* setup NFC_CTL register */
|
|
||||||
dev_info(info->device,
|
|
||||||
"data_width=%d, wr_dly=%d, rd_dly=%d\n",
|
|
||||||
(plat->data_width ? 16 : 8),
|
|
||||||
plat->wr_dly, plat->rd_dly);
|
|
||||||
|
|
||||||
val = (1 << NFC_PG_SIZE_OFFSET) |
|
|
||||||
(plat->data_width << NFC_NWIDTH_OFFSET) |
|
|
||||||
(plat->rd_dly << NFC_RDDLY_OFFSET) |
|
|
||||||
(plat->wr_dly << NFC_WRDLY_OFFSET);
|
|
||||||
dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val);
|
|
||||||
|
|
||||||
bfin_write_NFC_CTL(val);
|
|
||||||
SSYNC();
|
|
||||||
|
|
||||||
/* clear interrupt status */
|
|
||||||
bfin_write_NFC_IRQMASK(0x0);
|
|
||||||
SSYNC();
|
|
||||||
val = bfin_read_NFC_IRQSTAT();
|
|
||||||
bfin_write_NFC_IRQSTAT(val);
|
|
||||||
SSYNC();
|
|
||||||
|
|
||||||
/* DMA initialization */
|
|
||||||
if (bf5xx_nand_dma_init(info))
|
|
||||||
err = -ENXIO;
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Device management interface
|
|
||||||
*/
|
|
||||||
static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
|
|
||||||
{
|
|
||||||
struct mtd_info *mtd = nand_to_mtd(&info->chip);
|
|
||||||
struct mtd_partition *parts = info->platform->partitions;
|
|
||||||
int nr = info->platform->nr_partitions;
|
|
||||||
|
|
||||||
return mtd_device_register(mtd, parts, nr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bf5xx_nand_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct bf5xx_nand_info *info = to_nand_info(pdev);
|
|
||||||
|
|
||||||
/* first thing we need to do is release all our mtds
|
|
||||||
* and their partitions, then go through freeing the
|
|
||||||
* resources used
|
|
||||||
*/
|
|
||||||
nand_release(nand_to_mtd(&info->chip));
|
|
||||||
|
|
||||||
peripheral_free_list(bfin_nfc_pin_req);
|
|
||||||
bf5xx_nand_dma_remove(info);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bf5xx_nand_scan(struct mtd_info *mtd)
|
|
||||||
{
|
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = nand_scan_ident(mtd, 1, NULL);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (hardware_ecc) {
|
|
||||||
/*
|
|
||||||
* for nand with page size > 512B, think it as several sections with 512B
|
|
||||||
*/
|
|
||||||
if (likely(mtd->writesize >= 512)) {
|
|
||||||
chip->ecc.size = 512;
|
|
||||||
chip->ecc.bytes = 6;
|
|
||||||
chip->ecc.strength = 2;
|
|
||||||
} else {
|
|
||||||
chip->ecc.size = 256;
|
|
||||||
chip->ecc.bytes = 3;
|
|
||||||
chip->ecc.strength = 1;
|
|
||||||
bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET));
|
|
||||||
SSYNC();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nand_scan_tail(mtd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* bf5xx_nand_probe
|
|
||||||
*
|
|
||||||
* called by device layer when it finds a device matching
|
|
||||||
* one our driver can handled. This code checks to see if
|
|
||||||
* it can allocate all necessary resources then calls the
|
|
||||||
* nand layer to look for devices
|
|
||||||
*/
|
|
||||||
static int bf5xx_nand_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct bf5xx_nand_platform *plat = to_nand_plat(pdev);
|
|
||||||
struct bf5xx_nand_info *info = NULL;
|
|
||||||
struct nand_chip *chip = NULL;
|
|
||||||
struct mtd_info *mtd = NULL;
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "(%p)\n", pdev);
|
|
||||||
|
|
||||||
if (!plat) {
|
|
||||||
dev_err(&pdev->dev, "no platform specific information\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) {
|
|
||||||
dev_err(&pdev->dev, "requesting Peripherals failed\n");
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
|
||||||
if (info == NULL) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto out_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, info);
|
|
||||||
|
|
||||||
nand_hw_control_init(&info->controller);
|
|
||||||
|
|
||||||
info->device = &pdev->dev;
|
|
||||||
info->platform = plat;
|
|
||||||
|
|
||||||
/* initialise chip data struct */
|
|
||||||
chip = &info->chip;
|
|
||||||
mtd = nand_to_mtd(&info->chip);
|
|
||||||
|
|
||||||
if (plat->data_width)
|
|
||||||
chip->options |= NAND_BUSWIDTH_16;
|
|
||||||
|
|
||||||
chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN;
|
|
||||||
|
|
||||||
chip->read_buf = (plat->data_width) ?
|
|
||||||
bf5xx_nand_read_buf16 : bf5xx_nand_read_buf;
|
|
||||||
chip->write_buf = (plat->data_width) ?
|
|
||||||
bf5xx_nand_write_buf16 : bf5xx_nand_write_buf;
|
|
||||||
|
|
||||||
chip->read_byte = bf5xx_nand_read_byte;
|
|
||||||
|
|
||||||
chip->cmd_ctrl = bf5xx_nand_hwcontrol;
|
|
||||||
chip->dev_ready = bf5xx_nand_devready;
|
|
||||||
|
|
||||||
nand_set_controller_data(chip, mtd);
|
|
||||||
chip->controller = &info->controller;
|
|
||||||
|
|
||||||
chip->IO_ADDR_R = (void __iomem *) NFC_READ;
|
|
||||||
chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR;
|
|
||||||
|
|
||||||
chip->chip_delay = 0;
|
|
||||||
|
|
||||||
/* initialise mtd info data struct */
|
|
||||||
mtd->dev.parent = &pdev->dev;
|
|
||||||
|
|
||||||
/* initialise the hardware */
|
|
||||||
err = bf5xx_nand_hw_init(info);
|
|
||||||
if (err)
|
|
||||||
goto out_err;
|
|
||||||
|
|
||||||
/* setup hardware ECC data struct */
|
|
||||||
if (hardware_ecc) {
|
|
||||||
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
|
|
||||||
mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops);
|
|
||||||
#endif
|
|
||||||
chip->read_buf = bf5xx_nand_dma_read_buf;
|
|
||||||
chip->write_buf = bf5xx_nand_dma_write_buf;
|
|
||||||
chip->ecc.calculate = bf5xx_nand_calculate_ecc;
|
|
||||||
chip->ecc.correct = bf5xx_nand_correct_data;
|
|
||||||
chip->ecc.mode = NAND_ECC_HW;
|
|
||||||
chip->ecc.hwctl = bf5xx_nand_enable_hwecc;
|
|
||||||
chip->ecc.read_page_raw = bf5xx_nand_read_page_raw;
|
|
||||||
chip->ecc.write_page_raw = bf5xx_nand_write_page_raw;
|
|
||||||
} else {
|
|
||||||
chip->ecc.mode = NAND_ECC_SOFT;
|
|
||||||
chip->ecc.algo = NAND_ECC_HAMMING;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* scan hardware nand chip and setup mtd info data struct */
|
|
||||||
if (bf5xx_nand_scan(mtd)) {
|
|
||||||
err = -ENXIO;
|
|
||||||
goto out_err_nand_scan;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
|
|
||||||
chip->badblockpos = 63;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* add NAND partition */
|
|
||||||
bf5xx_nand_add_partition(info);
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "initialised ok\n");
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_err_nand_scan:
|
|
||||||
bf5xx_nand_dma_remove(info);
|
|
||||||
out_err:
|
|
||||||
peripheral_free_list(bfin_nfc_pin_req);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* driver device registration */
|
|
||||||
static struct platform_driver bf5xx_nand_driver = {
|
|
||||||
.probe = bf5xx_nand_probe,
|
|
||||||
.remove = bf5xx_nand_remove,
|
|
||||||
.driver = {
|
|
||||||
.name = DRV_NAME,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
module_platform_driver(bf5xx_nand_driver);
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_AUTHOR(DRV_AUTHOR);
|
|
||||||
MODULE_DESCRIPTION(DRV_DESC);
|
|
||||||
MODULE_ALIAS("platform:" DRV_NAME);
|
|
244
drivers/mtd/nand/core.c
Normal file
244
drivers/mtd/nand/core.c
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 Free Electrons
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||||
|
* Peter Pan <peterpandong@micron.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "nand: " fmt
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mtd/nand.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_isbad() - Check if a block is bad
|
||||||
|
* @nand: NAND device
|
||||||
|
* @pos: position pointing to the block we want to check
|
||||||
|
*
|
||||||
|
* Return: true if the block is bad, false otherwise.
|
||||||
|
*/
|
||||||
|
bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos)
|
||||||
|
{
|
||||||
|
if (nanddev_bbt_is_initialized(nand)) {
|
||||||
|
unsigned int entry;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
entry = nanddev_bbt_pos_to_entry(nand, pos);
|
||||||
|
status = nanddev_bbt_get_block_status(nand, entry);
|
||||||
|
/* Lazy block status retrieval */
|
||||||
|
if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) {
|
||||||
|
if (nand->ops->isbad(nand, pos))
|
||||||
|
status = NAND_BBT_BLOCK_FACTORY_BAD;
|
||||||
|
else
|
||||||
|
status = NAND_BBT_BLOCK_GOOD;
|
||||||
|
|
||||||
|
nanddev_bbt_set_block_status(nand, entry, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == NAND_BBT_BLOCK_WORN ||
|
||||||
|
status == NAND_BBT_BLOCK_FACTORY_BAD)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nand->ops->isbad(nand, pos);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_isbad);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_markbad() - Mark a block as bad
|
||||||
|
* @nand: NAND device
|
||||||
|
* @pos: position of the block to mark bad
|
||||||
|
*
|
||||||
|
* Mark a block bad. This function is updating the BBT if available and
|
||||||
|
* calls the low-level markbad hook (nand->ops->markbad()).
|
||||||
|
*
|
||||||
|
* Return: 0 in case of success, a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||||
|
unsigned int entry;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (nanddev_isbad(nand, pos))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = nand->ops->markbad(nand, pos);
|
||||||
|
if (ret)
|
||||||
|
pr_warn("failed to write BBM to block @%llx (err = %d)\n",
|
||||||
|
nanddev_pos_to_offs(nand, pos), ret);
|
||||||
|
|
||||||
|
if (!nanddev_bbt_is_initialized(nand))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
entry = nanddev_bbt_pos_to_entry(nand, pos);
|
||||||
|
ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = nanddev_bbt_update(nand);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (!ret)
|
||||||
|
mtd->ecc_stats.badblocks++;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_markbad);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_isreserved() - Check whether an eraseblock is reserved or not
|
||||||
|
* @nand: NAND device
|
||||||
|
* @pos: NAND position to test
|
||||||
|
*
|
||||||
|
* Checks whether the eraseblock pointed by @pos is reserved or not.
|
||||||
|
*
|
||||||
|
* Return: true if the eraseblock is reserved, false otherwise.
|
||||||
|
*/
|
||||||
|
bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos)
|
||||||
|
{
|
||||||
|
unsigned int entry;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (!nanddev_bbt_is_initialized(nand))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Return info from the table */
|
||||||
|
entry = nanddev_bbt_pos_to_entry(nand, pos);
|
||||||
|
status = nanddev_bbt_get_block_status(nand, entry);
|
||||||
|
return status == NAND_BBT_BLOCK_RESERVED;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_isreserved);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_erase() - Erase a NAND portion
|
||||||
|
* @nand: NAND device
|
||||||
|
* @pos: position of the block to erase
|
||||||
|
*
|
||||||
|
* Erases the block if it's not bad.
|
||||||
|
*
|
||||||
|
* Return: 0 in case of success, a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
|
||||||
|
{
|
||||||
|
if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) {
|
||||||
|
pr_warn("attempt to erase a bad/reserved block @%llx\n",
|
||||||
|
nanddev_pos_to_offs(nand, pos));
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nand->ops->erase(nand, pos);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_erase);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices
|
||||||
|
* @mtd: MTD device
|
||||||
|
* @einfo: erase request
|
||||||
|
*
|
||||||
|
* This is a simple mtd->_erase() implementation iterating over all blocks
|
||||||
|
* concerned by @einfo and calling nand->ops->erase() on each of them.
|
||||||
|
*
|
||||||
|
* Note that mtd->_erase should not be directly assigned to this helper,
|
||||||
|
* because there's no locking here. NAND specialized layers should instead
|
||||||
|
* implement there own wrapper around nanddev_mtd_erase() taking the
|
||||||
|
* appropriate lock before calling nanddev_mtd_erase().
|
||||||
|
*
|
||||||
|
* Return: 0 in case of success, a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo)
|
||||||
|
{
|
||||||
|
struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||||
|
struct nand_pos pos, last;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
nanddev_offs_to_pos(nand, einfo->addr, &pos);
|
||||||
|
nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last);
|
||||||
|
while (nanddev_pos_cmp(&pos, &last) <= 0) {
|
||||||
|
ret = nanddev_erase(nand, &pos);
|
||||||
|
if (ret) {
|
||||||
|
einfo->fail_addr = nanddev_pos_to_offs(nand, &pos);
|
||||||
|
einfo->state = MTD_ERASE_FAILED;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
nanddev_pos_next_eraseblock(nand, &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
einfo->state = MTD_ERASE_DONE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_mtd_erase);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_init() - Initialize a NAND device
|
||||||
|
* @nand: NAND device
|
||||||
|
* @ops: NAND device operations
|
||||||
|
* @owner: NAND device owner
|
||||||
|
*
|
||||||
|
* Initializes a NAND device object. Consistency checks are done on @ops and
|
||||||
|
* @nand->memorg. Also takes care of initializing the BBT.
|
||||||
|
*
|
||||||
|
* Return: 0 in case of success, a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
int nanddev_init(struct nand_device *nand, const struct nand_ops *ops,
|
||||||
|
struct module *owner)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||||
|
struct nand_memory_organization *memorg = nanddev_get_memorg(nand);
|
||||||
|
|
||||||
|
if (!nand || !ops)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!ops->erase || !ops->markbad || !ops->isbad)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!memorg->bits_per_cell || !memorg->pagesize ||
|
||||||
|
!memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun ||
|
||||||
|
!memorg->planes_per_lun || !memorg->luns_per_target ||
|
||||||
|
!memorg->ntargets)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
nand->rowconv.eraseblock_addr_shift =
|
||||||
|
fls(memorg->pages_per_eraseblock - 1);
|
||||||
|
nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) +
|
||||||
|
nand->rowconv.eraseblock_addr_shift;
|
||||||
|
|
||||||
|
nand->ops = ops;
|
||||||
|
|
||||||
|
mtd->type = memorg->bits_per_cell == 1 ?
|
||||||
|
MTD_NANDFLASH : MTD_MLCNANDFLASH;
|
||||||
|
mtd->flags = MTD_CAP_NANDFLASH;
|
||||||
|
mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock;
|
||||||
|
mtd->writesize = memorg->pagesize;
|
||||||
|
mtd->writebufsize = memorg->pagesize;
|
||||||
|
mtd->oobsize = memorg->oobsize;
|
||||||
|
mtd->size = nanddev_size(nand);
|
||||||
|
mtd->owner = owner;
|
||||||
|
|
||||||
|
return nanddev_bbt_init(nand);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_init);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nanddev_cleanup() - Release resources allocated in nanddev_init()
|
||||||
|
* @nand: NAND device
|
||||||
|
*
|
||||||
|
* Basically undoes what has been done in nanddev_init().
|
||||||
|
*/
|
||||||
|
void nanddev_cleanup(struct nand_device *nand)
|
||||||
|
{
|
||||||
|
if (nanddev_bbt_is_initialized(nand))
|
||||||
|
nanddev_bbt_cleanup(nand);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nanddev_cleanup);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Generic NAND framework");
|
||||||
|
MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* linux/drivers/mtd/onenand/generic.c
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005 Samsung Electronics
|
* Copyright (c) 2005 Samsung Electronics
|
||||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||||
*
|
*
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* linux/drivers/mtd/onenand/omap2.c
|
|
||||||
*
|
|
||||||
* OneNAND driver for OMAP2 / OMAP3
|
* OneNAND driver for OMAP2 / OMAP3
|
||||||
*
|
*
|
||||||
* Copyright © 2005-2006 Nokia Corporation
|
* Copyright © 2005-2006 Nokia Corporation
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* linux/drivers/mtd/onenand/onenand_base.c
|
|
||||||
*
|
|
||||||
* Copyright © 2005-2009 Samsung Electronics
|
* Copyright © 2005-2009 Samsung Electronics
|
||||||
* Copyright © 2007 Nokia Corporation
|
* Copyright © 2007 Nokia Corporation
|
||||||
*
|
*
|
||||||
|
@ -2143,7 +2141,6 @@ static int onenand_multiblock_erase_verify(struct mtd_info *mtd,
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printk(KERN_ERR "%s: Failed verify, block %d\n",
|
printk(KERN_ERR "%s: Failed verify, block %d\n",
|
||||||
__func__, onenand_block(this, addr));
|
__func__, onenand_block(this, addr));
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
instr->fail_addr = addr;
|
instr->fail_addr = addr;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -2172,8 +2169,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int bdry_block = 0;
|
int bdry_block = 0;
|
||||||
|
|
||||||
instr->state = MTD_ERASING;
|
|
||||||
|
|
||||||
if (ONENAND_IS_DDP(this)) {
|
if (ONENAND_IS_DDP(this)) {
|
||||||
loff_t bdry_addr = this->chipsize >> 1;
|
loff_t bdry_addr = this->chipsize >> 1;
|
||||||
if (addr < bdry_addr && (addr + len) > bdry_addr)
|
if (addr < bdry_addr && (addr + len) > bdry_addr)
|
||||||
|
@ -2187,7 +2182,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
|
||||||
printk(KERN_WARNING "%s: attempt to erase a bad block "
|
printk(KERN_WARNING "%s: attempt to erase a bad block "
|
||||||
"at addr 0x%012llx\n",
|
"at addr 0x%012llx\n",
|
||||||
__func__, (unsigned long long) addr);
|
__func__, (unsigned long long) addr);
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
len -= block_size;
|
len -= block_size;
|
||||||
|
@ -2227,7 +2221,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
|
||||||
printk(KERN_ERR "%s: Failed multiblock erase, "
|
printk(KERN_ERR "%s: Failed multiblock erase, "
|
||||||
"block %d\n", __func__,
|
"block %d\n", __func__,
|
||||||
onenand_block(this, addr));
|
onenand_block(this, addr));
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -2247,7 +2240,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printk(KERN_ERR "%s: Failed erase, block %d\n",
|
printk(KERN_ERR "%s: Failed erase, block %d\n",
|
||||||
__func__, onenand_block(this, addr));
|
__func__, onenand_block(this, addr));
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -2259,7 +2251,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
|
||||||
/* verify */
|
/* verify */
|
||||||
verify_instr.len = eb_count * block_size;
|
verify_instr.len = eb_count * block_size;
|
||||||
if (onenand_multiblock_erase_verify(mtd, &verify_instr)) {
|
if (onenand_multiblock_erase_verify(mtd, &verify_instr)) {
|
||||||
instr->state = verify_instr.state;
|
|
||||||
instr->fail_addr = verify_instr.fail_addr;
|
instr->fail_addr = verify_instr.fail_addr;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -2294,8 +2285,6 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd,
|
||||||
region_end = region->offset + region->erasesize * region->numblocks;
|
region_end = region->offset + region->erasesize * region->numblocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
instr->state = MTD_ERASING;
|
|
||||||
|
|
||||||
/* Loop through the blocks */
|
/* Loop through the blocks */
|
||||||
while (len) {
|
while (len) {
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
@ -2305,7 +2294,6 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd,
|
||||||
printk(KERN_WARNING "%s: attempt to erase a bad block "
|
printk(KERN_WARNING "%s: attempt to erase a bad block "
|
||||||
"at addr 0x%012llx\n",
|
"at addr 0x%012llx\n",
|
||||||
__func__, (unsigned long long) addr);
|
__func__, (unsigned long long) addr);
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2318,7 +2306,6 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd,
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printk(KERN_ERR "%s: Failed erase, block %d\n",
|
printk(KERN_ERR "%s: Failed erase, block %d\n",
|
||||||
__func__, onenand_block(this, addr));
|
__func__, onenand_block(this, addr));
|
||||||
instr->state = MTD_ERASE_FAILED;
|
|
||||||
instr->fail_addr = addr;
|
instr->fail_addr = addr;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -2407,12 +2394,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||||
/* Deselect and wake up anyone waiting on the device */
|
/* Deselect and wake up anyone waiting on the device */
|
||||||
onenand_release_device(mtd);
|
onenand_release_device(mtd);
|
||||||
|
|
||||||
/* Do call back function */
|
|
||||||
if (!ret) {
|
|
||||||
instr->state = MTD_ERASE_DONE;
|
|
||||||
mtd_erase_callback(instr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* linux/drivers/mtd/onenand/onenand_bbt.c
|
|
||||||
*
|
|
||||||
* Bad Block Table support for the OneNAND driver
|
* Bad Block Table support for the OneNAND driver
|
||||||
*
|
*
|
||||||
* Copyright(c) 2005 Samsung Electronics
|
* Copyright(c) 2005 Samsung Electronics
|
537
drivers/mtd/nand/raw/Kconfig
Normal file
537
drivers/mtd/nand/raw/Kconfig
Normal file
|
@ -0,0 +1,537 @@
|
||||||
|
config MTD_NAND_ECC
|
||||||
|
tristate
|
||||||
|
|
||||||
|
config MTD_NAND_ECC_SMC
|
||||||
|
bool "NAND ECC Smart Media byte order"
|
||||||
|
depends on MTD_NAND_ECC
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Software ECC according to the Smart Media Specification.
|
||||||
|
The original Linux implementation had byte 0 and 1 swapped.
|
||||||
|
|
||||||
|
|
||||||
|
menuconfig MTD_NAND
|
||||||
|
tristate "Raw/Parallel NAND Device Support"
|
||||||
|
depends on MTD
|
||||||
|
select MTD_NAND_ECC
|
||||||
|
help
|
||||||
|
This enables support for accessing all type of raw/parallel
|
||||||
|
NAND flash devices. For further information see
|
||||||
|
<http://www.linux-mtd.infradead.org/doc/nand.html>.
|
||||||
|
|
||||||
|
if MTD_NAND
|
||||||
|
|
||||||
|
config MTD_NAND_BCH
|
||||||
|
tristate
|
||||||
|
select BCH
|
||||||
|
depends on MTD_NAND_ECC_BCH
|
||||||
|
default MTD_NAND
|
||||||
|
|
||||||
|
config MTD_NAND_ECC_BCH
|
||||||
|
bool "Support software BCH ECC"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This enables support for software BCH error correction. Binary BCH
|
||||||
|
codes are more powerful and cpu intensive than traditional Hamming
|
||||||
|
ECC codes. They are used with NAND devices requiring more than 1 bit
|
||||||
|
of error correction.
|
||||||
|
|
||||||
|
config MTD_SM_COMMON
|
||||||
|
tristate
|
||||||
|
default n
|
||||||
|
|
||||||
|
config MTD_NAND_DENALI
|
||||||
|
tristate
|
||||||
|
|
||||||
|
config MTD_NAND_DENALI_PCI
|
||||||
|
tristate "Support Denali NAND controller on Intel Moorestown"
|
||||||
|
select MTD_NAND_DENALI
|
||||||
|
depends on HAS_DMA && PCI
|
||||||
|
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"
|
||||||
|
select MTD_NAND_DENALI
|
||||||
|
depends on HAS_DMA && HAVE_CLK && OF
|
||||||
|
help
|
||||||
|
Enable the driver for NAND flash on platforms using a Denali NAND
|
||||||
|
controller as a DT device.
|
||||||
|
|
||||||
|
config MTD_NAND_GPIO
|
||||||
|
tristate "GPIO assisted NAND Flash driver"
|
||||||
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
|
depends on HAS_IOMEM
|
||||||
|
help
|
||||||
|
This enables a NAND flash driver where control signals are
|
||||||
|
connected to GPIO pins, and commands and data are communicated
|
||||||
|
via a memory mapped interface.
|
||||||
|
|
||||||
|
config MTD_NAND_AMS_DELTA
|
||||||
|
tristate "NAND Flash device on Amstrad E3"
|
||||||
|
depends on MACH_AMS_DELTA
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Support for NAND flash on Amstrad E3 (Delta).
|
||||||
|
|
||||||
|
config MTD_NAND_OMAP2
|
||||||
|
tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
|
||||||
|
depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
|
||||||
|
help
|
||||||
|
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
|
||||||
|
and Keystone platforms.
|
||||||
|
|
||||||
|
config MTD_NAND_OMAP_BCH
|
||||||
|
depends on MTD_NAND_OMAP2
|
||||||
|
bool "Support hardware based BCH error correction"
|
||||||
|
default n
|
||||||
|
select BCH
|
||||||
|
help
|
||||||
|
This config enables the ELM hardware engine, which can be used to
|
||||||
|
locate and correct errors when using BCH ECC scheme. This offloads
|
||||||
|
the cpu from doing ECC error searching and correction. However some
|
||||||
|
legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
|
||||||
|
so this is optional for them.
|
||||||
|
|
||||||
|
config MTD_NAND_OMAP_BCH_BUILD
|
||||||
|
def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH
|
||||||
|
|
||||||
|
config MTD_NAND_RICOH
|
||||||
|
tristate "Ricoh xD card reader"
|
||||||
|
default n
|
||||||
|
depends on PCI
|
||||||
|
select MTD_SM_COMMON
|
||||||
|
help
|
||||||
|
Enable support for Ricoh R5C852 xD card reader
|
||||||
|
You also need to enable ether
|
||||||
|
NAND SSFDC (SmartMedia) read only translation layer' or new
|
||||||
|
expermental, readwrite
|
||||||
|
'SmartMedia/xD new translation layer'
|
||||||
|
|
||||||
|
config MTD_NAND_AU1550
|
||||||
|
tristate "Au1550/1200 NAND support"
|
||||||
|
depends on MIPS_ALCHEMY
|
||||||
|
help
|
||||||
|
This enables the driver for the NAND flash controller on the
|
||||||
|
AMD/Alchemy 1550 SOC.
|
||||||
|
|
||||||
|
config MTD_NAND_S3C2410
|
||||||
|
tristate "NAND Flash support for Samsung S3C SoCs"
|
||||||
|
depends on ARCH_S3C24XX || ARCH_S3C64XX
|
||||||
|
help
|
||||||
|
This enables the NAND flash controller on the S3C24xx and S3C64xx
|
||||||
|
SoCs
|
||||||
|
|
||||||
|
No board specific support is done by this driver, each board
|
||||||
|
must advertise a platform_device for the driver to attach.
|
||||||
|
|
||||||
|
config MTD_NAND_S3C2410_DEBUG
|
||||||
|
bool "Samsung S3C NAND driver debug"
|
||||||
|
depends on MTD_NAND_S3C2410
|
||||||
|
help
|
||||||
|
Enable debugging of the S3C NAND driver
|
||||||
|
|
||||||
|
config MTD_NAND_NDFC
|
||||||
|
tristate "NDFC NanD Flash Controller"
|
||||||
|
depends on 4xx
|
||||||
|
select MTD_NAND_ECC_SMC
|
||||||
|
help
|
||||||
|
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
||||||
|
|
||||||
|
config MTD_NAND_S3C2410_CLKSTOP
|
||||||
|
bool "Samsung S3C NAND IDLE clock stop"
|
||||||
|
depends on MTD_NAND_S3C2410
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Stop the clock to the NAND controller when there is no chip
|
||||||
|
selected to save power. This will mean there is a small delay
|
||||||
|
when the is NAND chip selected or released, but will save
|
||||||
|
approximately 5mA of power when there is nothing happening.
|
||||||
|
|
||||||
|
config MTD_NAND_TANGO
|
||||||
|
tristate "NAND Flash support for Tango chips"
|
||||||
|
depends on ARCH_TANGO || COMPILE_TEST
|
||||||
|
depends on HAS_DMA
|
||||||
|
help
|
||||||
|
Enables the NAND Flash controller on Tango chips.
|
||||||
|
|
||||||
|
config MTD_NAND_DISKONCHIP
|
||||||
|
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)"
|
||||||
|
depends on HAS_IOMEM
|
||||||
|
select REED_SOLOMON
|
||||||
|
select REED_SOLOMON_DEC16
|
||||||
|
help
|
||||||
|
This is a reimplementation of M-Systems DiskOnChip 2000,
|
||||||
|
Millennium and Millennium Plus as a standard NAND device driver,
|
||||||
|
as opposed to the earlier self-contained MTD device drivers.
|
||||||
|
This should enable, among other things, proper JFFS2 operation on
|
||||||
|
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'.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
(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.
|
||||||
|
|
||||||
|
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'.
|
||||||
|
|
||||||
|
config MTD_NAND_DISKONCHIP_BBTWRITE
|
||||||
|
bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
|
||||||
|
depends on MTD_NAND_DISKONCHIP
|
||||||
|
help
|
||||||
|
On DiskOnChip devices shipped with the INFTL filesystem (Millennium
|
||||||
|
and 2000 TSOP/Alon), Linux reserves some space at the end of the
|
||||||
|
device for the Bad Block Table (BBT). If you have existing INFTL
|
||||||
|
data on your device (created by non-Linux tools such as M-Systems'
|
||||||
|
DOS drivers), your data might overlap the area Linux wants to use for
|
||||||
|
the BBT. If this is a concern for you, leave this option disabled and
|
||||||
|
Linux will not write BBT data into this area.
|
||||||
|
The downside of leaving this option disabled is that if bad blocks
|
||||||
|
are detected by Linux, they will not be recorded in the BBT, which
|
||||||
|
could cause future problems.
|
||||||
|
Once you enable this option, new filesystems (INFTL or others, created
|
||||||
|
in Linux or other operating systems) will not use the reserved area.
|
||||||
|
The only reason not to enable this option is to prevent damage to
|
||||||
|
preexisting filesystems.
|
||||||
|
Even if you leave this disabled, you can enable BBT writes at module
|
||||||
|
load time (assuming you build diskonchip as a module) with the module
|
||||||
|
parameter "inftl_bbt_write=1".
|
||||||
|
|
||||||
|
config MTD_NAND_DOCG4
|
||||||
|
tristate "Support for DiskOnChip G4"
|
||||||
|
depends on HAS_IOMEM
|
||||||
|
select BCH
|
||||||
|
select BITREVERSE
|
||||||
|
help
|
||||||
|
Support for diskonchip G4 nand flash, found in various smartphones and
|
||||||
|
PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba
|
||||||
|
Portege G900, Asus P526, and O2 XDA Zinc.
|
||||||
|
|
||||||
|
With this driver you will be able to use UBI and create a ubifs on the
|
||||||
|
device, so you may wish to consider enabling UBI and UBIFS as well.
|
||||||
|
|
||||||
|
These devices ship with the Mys/Sandisk SAFTL formatting, for which
|
||||||
|
there is currently no mtd parser, so you may want to use command line
|
||||||
|
partitioning to segregate write-protected blocks. On the Treo680, the
|
||||||
|
first five erase blocks (256KiB each) are write-protected, followed
|
||||||
|
by the block containing the saftl partition table. This is probably
|
||||||
|
typical.
|
||||||
|
|
||||||
|
config MTD_NAND_SHARPSL
|
||||||
|
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
|
||||||
|
depends on ARCH_PXA
|
||||||
|
|
||||||
|
config MTD_NAND_CAFE
|
||||||
|
tristate "NAND support for OLPC CAFÉ chip"
|
||||||
|
depends on PCI
|
||||||
|
select REED_SOLOMON
|
||||||
|
select REED_SOLOMON_DEC16
|
||||||
|
help
|
||||||
|
Use NAND flash attached to the CAFÉ chip designed for the OLPC
|
||||||
|
laptop.
|
||||||
|
|
||||||
|
config MTD_NAND_CS553X
|
||||||
|
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
|
||||||
|
depends on X86_32
|
||||||
|
depends on !UML && HAS_IOMEM
|
||||||
|
help
|
||||||
|
The CS553x companion chips for the AMD Geode processor
|
||||||
|
include NAND flash controllers with built-in hardware ECC
|
||||||
|
capabilities; enabling this option will allow you to use
|
||||||
|
these. The driver will check the MSRs to verify that the
|
||||||
|
controller is enabled for NAND, and currently requires that
|
||||||
|
the controller be in MMIO mode.
|
||||||
|
|
||||||
|
If you say "m", the module will be called cs553x_nand.
|
||||||
|
|
||||||
|
config MTD_NAND_ATMEL
|
||||||
|
tristate "Support for NAND Flash / SmartMedia on AT91"
|
||||||
|
depends on ARCH_AT91
|
||||||
|
select MFD_ATMEL_SMC
|
||||||
|
help
|
||||||
|
Enables support for NAND Flash / Smart Media Card interface
|
||||||
|
on Atmel AT91 processors.
|
||||||
|
|
||||||
|
config MTD_NAND_MARVELL
|
||||||
|
tristate "NAND controller support on Marvell boards"
|
||||||
|
depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
|
||||||
|
COMPILE_TEST
|
||||||
|
depends on HAS_IOMEM && HAS_DMA
|
||||||
|
help
|
||||||
|
This enables the NAND flash controller driver for Marvell boards,
|
||||||
|
including:
|
||||||
|
- PXA3xx processors (NFCv1)
|
||||||
|
- 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
|
||||||
|
- 64-bit Aramda platforms (7k, 8k) (NFCv2)
|
||||||
|
|
||||||
|
config MTD_NAND_SLC_LPC32XX
|
||||||
|
tristate "NXP LPC32xx SLC Controller"
|
||||||
|
depends on ARCH_LPC32XX
|
||||||
|
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
|
||||||
|
reference board which contains a NAND256R3A2CZA6 chip.
|
||||||
|
|
||||||
|
Please check the actual NAND chip connected and its support
|
||||||
|
by the SLC NAND controller.
|
||||||
|
|
||||||
|
config MTD_NAND_MLC_LPC32XX
|
||||||
|
tristate "NXP LPC32xx MLC Controller"
|
||||||
|
depends on ARCH_LPC32XX
|
||||||
|
help
|
||||||
|
Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
|
||||||
|
controller. This is the default for the WORK92105 controller
|
||||||
|
board.
|
||||||
|
|
||||||
|
Please check the actual NAND chip connected and its support
|
||||||
|
by the MLC NAND controller.
|
||||||
|
|
||||||
|
config MTD_NAND_CM_X270
|
||||||
|
tristate "Support for NAND Flash on CM-X270 modules"
|
||||||
|
depends on MACH_ARMCORE
|
||||||
|
|
||||||
|
config MTD_NAND_PASEMI
|
||||||
|
tristate "NAND support for PA Semi PWRficient"
|
||||||
|
depends on PPC_PASEMI
|
||||||
|
help
|
||||||
|
Enables support for NAND Flash interface on PA Semi PWRficient
|
||||||
|
based boards
|
||||||
|
|
||||||
|
config MTD_NAND_TMIO
|
||||||
|
tristate "NAND Flash device on Toshiba Mobile IO Controller"
|
||||||
|
depends on MFD_TMIO
|
||||||
|
help
|
||||||
|
Support for NAND flash connected to a Toshiba Mobile IO
|
||||||
|
Controller in some PDAs, including the Sharp SL6000x.
|
||||||
|
|
||||||
|
config MTD_NAND_NANDSIM
|
||||||
|
tristate "Support for NAND Flash Simulator"
|
||||||
|
help
|
||||||
|
The simulator may simulate various NAND flash chips for the
|
||||||
|
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.
|
||||||
|
|
||||||
|
config MTD_NAND_BRCMNAND
|
||||||
|
tristate "Broadcom STB NAND controller"
|
||||||
|
depends on ARM || ARM64 || MIPS
|
||||||
|
help
|
||||||
|
Enables the Broadcom NAND controller driver. The controller was
|
||||||
|
originally designed for Set-Top Box but is used on various BCM7xxx,
|
||||||
|
BCM3xxx, BCM63xxx, iProc/Cygnus and more.
|
||||||
|
|
||||||
|
config MTD_NAND_BCM47XXNFLASH
|
||||||
|
tristate "Support for NAND flash on BCM4706 BCMA bus"
|
||||||
|
depends on BCMA_NFLASH
|
||||||
|
help
|
||||||
|
BCMA bus can have various flash memories attached, they are
|
||||||
|
registered by bcma as platform devices. This enables driver for
|
||||||
|
NAND flash memories. For now only BCM4706 is supported.
|
||||||
|
|
||||||
|
config MTD_NAND_PLATFORM
|
||||||
|
tristate "Support for generic platform NAND driver"
|
||||||
|
depends on HAS_IOMEM
|
||||||
|
help
|
||||||
|
This implements a generic NAND driver for on-SOC platform
|
||||||
|
devices. You will need to provide platform-specific functions
|
||||||
|
via platform_data.
|
||||||
|
|
||||||
|
config MTD_NAND_ORION
|
||||||
|
tristate "NAND Flash support for Marvell Orion SoC"
|
||||||
|
depends on PLAT_ORION
|
||||||
|
help
|
||||||
|
This enables the NAND flash controller on Orion machines.
|
||||||
|
|
||||||
|
No board specific support is done by this driver, each board
|
||||||
|
must advertise a platform_device for the driver to attach.
|
||||||
|
|
||||||
|
config MTD_NAND_OXNAS
|
||||||
|
tristate "NAND Flash support for Oxford Semiconductor SoC"
|
||||||
|
depends on ARCH_OXNAS || COMPILE_TEST
|
||||||
|
depends on HAS_IOMEM
|
||||||
|
help
|
||||||
|
This enables the NAND flash controller on Oxford Semiconductor SoCs.
|
||||||
|
|
||||||
|
config MTD_NAND_FSL_ELBC
|
||||||
|
tristate "NAND support for Freescale eLBC controllers"
|
||||||
|
depends on FSL_SOC
|
||||||
|
select FSL_LBC
|
||||||
|
help
|
||||||
|
Various Freescale chips, including the 8313, include a NAND Flash
|
||||||
|
Controller Module with built-in hardware ECC capabilities.
|
||||||
|
Enabling this option will enable you to use this to control
|
||||||
|
external NAND devices.
|
||||||
|
|
||||||
|
config MTD_NAND_FSL_IFC
|
||||||
|
tristate "NAND support for Freescale IFC controller"
|
||||||
|
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
|
||||||
|
select FSL_IFC
|
||||||
|
select MEMORY
|
||||||
|
help
|
||||||
|
Various Freescale chips e.g P1010, include a NAND Flash machine
|
||||||
|
with built-in hardware ECC capabilities.
|
||||||
|
Enabling this option will enable you to use this to control
|
||||||
|
external NAND devices.
|
||||||
|
|
||||||
|
config MTD_NAND_FSL_UPM
|
||||||
|
tristate "Support for NAND on Freescale UPM"
|
||||||
|
depends on PPC_83xx || PPC_85xx
|
||||||
|
select FSL_LBC
|
||||||
|
help
|
||||||
|
Enables support for NAND Flash chips wired onto Freescale PowerPC
|
||||||
|
processor localbus with User-Programmable Machine support.
|
||||||
|
|
||||||
|
config MTD_NAND_MPC5121_NFC
|
||||||
|
tristate "MPC5121 built-in NAND Flash Controller support"
|
||||||
|
depends on PPC_MPC512x
|
||||||
|
help
|
||||||
|
This enables the driver for the NAND flash controller on the
|
||||||
|
MPC5121 SoC.
|
||||||
|
|
||||||
|
config MTD_NAND_VF610_NFC
|
||||||
|
tristate "Support for Freescale NFC for VF610/MPC5125"
|
||||||
|
depends on (SOC_VF610 || COMPILE_TEST)
|
||||||
|
depends on HAS_IOMEM
|
||||||
|
help
|
||||||
|
Enables support for NAND Flash Controller on some Freescale
|
||||||
|
processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
|
||||||
|
The driver supports a maximum 2k page size. With 2k pages and
|
||||||
|
64 bytes or more of OOB, hardware ECC with up to 32-bit error
|
||||||
|
correction is supported. Hardware ECC is only enabled through
|
||||||
|
device tree.
|
||||||
|
|
||||||
|
config MTD_NAND_MXC
|
||||||
|
tristate "MXC NAND support"
|
||||||
|
depends on ARCH_MXC
|
||||||
|
help
|
||||||
|
This enables the driver for the NAND flash controller on the
|
||||||
|
MXC processors.
|
||||||
|
|
||||||
|
config MTD_NAND_SH_FLCTL
|
||||||
|
tristate "Support for NAND on Renesas SuperH FLCTL"
|
||||||
|
depends on SUPERH || COMPILE_TEST
|
||||||
|
depends on HAS_IOMEM
|
||||||
|
depends on HAS_DMA
|
||||||
|
help
|
||||||
|
Several Renesas SuperH CPU has FLCTL. This option enables support
|
||||||
|
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
|
||||||
|
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
|
||||||
|
help
|
||||||
|
This enables the NAND flash controller on the TXx9 SoCs.
|
||||||
|
|
||||||
|
config MTD_NAND_SOCRATES
|
||||||
|
tristate "Support for NAND on Socrates board"
|
||||||
|
depends on SOCRATES
|
||||||
|
help
|
||||||
|
Enables support for NAND Flash chips wired onto Socrates board.
|
||||||
|
|
||||||
|
config MTD_NAND_NUC900
|
||||||
|
tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
|
||||||
|
depends on ARCH_W90X900
|
||||||
|
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
|
||||||
|
help
|
||||||
|
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
|
||||||
|
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
|
||||||
|
help
|
||||||
|
Enables support for NAND Flash chips on the ST Microelectronics
|
||||||
|
Flexible Static Memory Controller (FSMC)
|
||||||
|
|
||||||
|
config MTD_NAND_XWAY
|
||||||
|
bool "Support for NAND on Lantiq XWAY SoC"
|
||||||
|
depends on LANTIQ && SOC_TYPE_XWAY
|
||||||
|
help
|
||||||
|
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
|
||||||
|
to the External Bus Unit (EBU).
|
||||||
|
|
||||||
|
config MTD_NAND_SUNXI
|
||||||
|
tristate "Support for NAND on Allwinner SoCs"
|
||||||
|
depends on ARCH_SUNXI
|
||||||
|
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_DMA
|
||||||
|
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
|
||||||
|
help
|
||||||
|
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
|
||||||
|
controller. This controller is found on IPQ806x SoC.
|
||||||
|
|
||||||
|
config MTD_NAND_MTK
|
||||||
|
tristate "Support for NAND controller on MTK SoCs"
|
||||||
|
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||||
|
depends on HAS_DMA
|
||||||
|
help
|
||||||
|
Enables support for NAND controller on MTK SoCs.
|
||||||
|
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
|
||||||
|
|
||||||
|
endif # MTD_NAND
|
66
drivers/mtd/nand/raw/Makefile
Normal file
66
drivers/mtd/nand/raw/Makefile
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
obj-$(CONFIG_MTD_NAND) += nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
|
||||||
|
obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel/
|
||||||
|
obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
|
||||||
|
omap2_nand-objs := omap2.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
|
||||||
|
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
|
||||||
|
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
|
||||||
|
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
|
||||||
|
|
||||||
|
nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
|
||||||
|
nand-objs += nand_amd.o
|
||||||
|
nand-objs += nand_hynix.o
|
||||||
|
nand-objs += nand_macronix.o
|
||||||
|
nand-objs += nand_micron.o
|
||||||
|
nand-objs += nand_samsung.o
|
||||||
|
nand-objs += nand_toshiba.o
|
|
@ -1,11 +1,12 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/nand/ams-delta.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
|
* Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
|
||||||
*
|
*
|
||||||
* Derived from drivers/mtd/toto.c
|
* Derived from drivers/mtd/nand/toto.c (removed in v2.6.28)
|
||||||
|
* Copyright (c) 2003 Texas Instruments
|
||||||
|
* Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
|
||||||
|
*
|
||||||
* Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
|
* Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
|
||||||
* Partially stolen from drivers/mtd/nand/plat_nand.c
|
* Partially stolen from plat_nand.c
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
@ -185,7 +186,7 @@ static int ams_delta_init(struct platform_device *pdev)
|
||||||
/* Allocate memory for MTD device structure and private data */
|
/* Allocate memory for MTD device structure and private data */
|
||||||
this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
|
this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
|
||||||
if (!this) {
|
if (!this) {
|
||||||
printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n");
|
pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +220,7 @@ static int ams_delta_init(struct platform_device *pdev)
|
||||||
this->dev_ready = ams_delta_nand_ready;
|
this->dev_ready = ams_delta_nand_ready;
|
||||||
} else {
|
} else {
|
||||||
this->dev_ready = NULL;
|
this->dev_ready = NULL;
|
||||||
printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n");
|
pr_notice("Couldn't request gpio for Delta NAND ready.\n");
|
||||||
}
|
}
|
||||||
/* 25 us command delay time */
|
/* 25 us command delay time */
|
||||||
this->chip_delay = 30;
|
this->chip_delay = 30;
|
|
@ -9,10 +9,10 @@
|
||||||
*
|
*
|
||||||
* Copyright 2003 Rick Bronson
|
* Copyright 2003 Rick Bronson
|
||||||
*
|
*
|
||||||
* Derived from drivers/mtd/nand/autcpu12.c
|
* Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
|
||||||
* Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
|
* Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||||
*
|
*
|
||||||
* Derived from drivers/mtd/spia.c
|
* Derived from drivers/mtd/spia.c (removed in v3.8)
|
||||||
* Copyright 2000 Steven J. Hill (sjhill@cotw.com)
|
* Copyright 2000 Steven J. Hill (sjhill@cotw.com)
|
||||||
*
|
*
|
||||||
*
|
*
|
|
@ -9,10 +9,10 @@
|
||||||
*
|
*
|
||||||
* Copyright 2003 Rick Bronson
|
* Copyright 2003 Rick Bronson
|
||||||
*
|
*
|
||||||
* Derived from drivers/mtd/nand/autcpu12.c
|
* Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
|
||||||
* Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
|
* Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||||
*
|
*
|
||||||
* Derived from drivers/mtd/spia.c
|
* Derived from drivers/mtd/spia.c (removed in v3.8)
|
||||||
* Copyright 2000 Steven J. Hill (sjhill@cotw.com)
|
* Copyright 2000 Steven J. Hill (sjhill@cotw.com)
|
||||||
*
|
*
|
||||||
* Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
|
* Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
|
|
@ -9,10 +9,10 @@
|
||||||
*
|
*
|
||||||
* Copyright © 2003 Rick Bronson
|
* Copyright © 2003 Rick Bronson
|
||||||
*
|
*
|
||||||
* Derived from drivers/mtd/nand/autcpu12.c
|
* Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
|
||||||
* Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
|
* Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||||
*
|
*
|
||||||
* Derived from drivers/mtd/spia.c
|
* Derived from drivers/mtd/spia.c (removed in v3.8)
|
||||||
* Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
|
* Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
|
||||||
*
|
*
|
||||||
*
|
*
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/nand/au1550nd.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 Embedded Edge, LLC
|
* Copyright (C) 2004 Embedded Edge, LLC
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
|
@ -392,8 +392,8 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
|
||||||
b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte;
|
b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte;
|
||||||
b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf;
|
b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf;
|
||||||
b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf;
|
b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf;
|
||||||
b47n->nand_chip.onfi_set_features = nand_onfi_get_set_features_notsupp;
|
b47n->nand_chip.set_features = nand_get_set_features_notsupp;
|
||||||
b47n->nand_chip.onfi_get_features = nand_onfi_get_set_features_notsupp;
|
b47n->nand_chip.get_features = nand_get_set_features_notsupp;
|
||||||
|
|
||||||
nand_chip->chip_delay = 50;
|
nand_chip->chip_delay = 50;
|
||||||
b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH;
|
b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH;
|
|
@ -2297,7 +2297,11 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return mtd_device_register(mtd, NULL, 0);
|
ret = mtd_device_register(mtd, NULL, 0);
|
||||||
|
if (ret)
|
||||||
|
nand_cleanup(chip);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
|
static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
|
|
@ -645,8 +645,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
||||||
cafe->nand.read_buf = cafe_read_buf;
|
cafe->nand.read_buf = cafe_read_buf;
|
||||||
cafe->nand.write_buf = cafe_write_buf;
|
cafe->nand.write_buf = cafe_write_buf;
|
||||||
cafe->nand.select_chip = cafe_select_chip;
|
cafe->nand.select_chip = cafe_select_chip;
|
||||||
cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp;
|
cafe->nand.set_features = nand_get_set_features_notsupp;
|
||||||
cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp;
|
cafe->nand.get_features = nand_get_set_features_notsupp;
|
||||||
|
|
||||||
cafe->nand.chip_delay = 0;
|
cafe->nand.chip_delay = 0;
|
||||||
|
|
||||||
|
@ -751,8 +751,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
||||||
cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
|
cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
|
||||||
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
|
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
|
pr_warn("Unexpected NAND flash writesize %d. Aborting\n",
|
||||||
mtd->writesize);
|
mtd->writesize);
|
||||||
goto out_free_dma;
|
goto out_free_dma;
|
||||||
}
|
}
|
||||||
cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
|
cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||||
|
@ -774,10 +774,14 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
||||||
pci_set_drvdata(pdev, mtd);
|
pci_set_drvdata(pdev, mtd);
|
||||||
|
|
||||||
mtd->name = "cafe_nand";
|
mtd->name = "cafe_nand";
|
||||||
mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
|
err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
|
||||||
|
if (err)
|
||||||
|
goto out_cleanup_nand;
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
out_cleanup_nand:
|
||||||
|
nand_cleanup(&cafe->nand);
|
||||||
out_free_dma:
|
out_free_dma:
|
||||||
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
||||||
out_irq:
|
out_irq:
|
|
@ -1,10 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* linux/drivers/mtd/nand/cmx270-nand.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2006 Compulab, Ltd.
|
* Copyright (C) 2006 Compulab, Ltd.
|
||||||
* Mike Rapoport <mike@compulab.co.il>
|
* Mike Rapoport <mike@compulab.co.il>
|
||||||
*
|
*
|
||||||
* Derived from drivers/mtd/nand/h1910.c
|
* Derived from drivers/mtd/nand/h1910.c (removed in v3.10)
|
||||||
* Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
|
* Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
|
||||||
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
|
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||||
*
|
*
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/nand/cs553x_nand.c
|
|
||||||
*
|
|
||||||
* (C) 2005, 2006 Red Hat Inc.
|
* (C) 2005, 2006 Red Hat Inc.
|
||||||
*
|
*
|
||||||
* Author: David Woodhouse <dwmw2@infradead.org>
|
* Author: David Woodhouse <dwmw2@infradead.org>
|
||||||
|
@ -189,10 +187,11 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
|
||||||
struct nand_chip *this;
|
struct nand_chip *this;
|
||||||
struct mtd_info *new_mtd;
|
struct mtd_info *new_mtd;
|
||||||
|
|
||||||
printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr);
|
pr_notice("Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n",
|
||||||
|
cs, mmio ? "MM" : "P", adr);
|
||||||
|
|
||||||
if (!mmio) {
|
if (!mmio) {
|
||||||
printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n");
|
pr_notice("PIO mode not yet implemented for CS553X NAND controller\n");
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +210,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
|
||||||
/* map physical address */
|
/* map physical address */
|
||||||
this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
|
this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
|
||||||
if (!this->IO_ADDR_R) {
|
if (!this->IO_ADDR_R) {
|
||||||
printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr);
|
pr_warn("ioremap cs553x NAND @0x%08lx failed\n", adr);
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto out_mtd;
|
goto out_mtd;
|
||||||
}
|
}
|
||||||
|
@ -295,7 +294,7 @@ static int __init cs553x_init(void)
|
||||||
/* If it doesn't have the NAND controller enabled, abort */
|
/* If it doesn't have the NAND controller enabled, abort */
|
||||||
rdmsrl(MSR_DIVIL_BALL_OPTS, val);
|
rdmsrl(MSR_DIVIL_BALL_OPTS, val);
|
||||||
if (val & PIN_OPT_IDE) {
|
if (val & PIN_OPT_IDE) {
|
||||||
printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n");
|
pr_info("CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n");
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -826,7 +826,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
|
||||||
else
|
else
|
||||||
ret = mtd_device_register(mtd, NULL, 0);
|
ret = mtd_device_register(mtd, NULL, 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err;
|
goto err_cleanup_nand;
|
||||||
|
|
||||||
val = davinci_nand_readl(info, NRCSR_OFFSET);
|
val = davinci_nand_readl(info, NRCSR_OFFSET);
|
||||||
dev_info(&pdev->dev, "controller rev. %d.%d\n",
|
dev_info(&pdev->dev, "controller rev. %d.%d\n",
|
||||||
|
@ -834,6 +834,9 @@ static int nand_davinci_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_cleanup_nand:
|
||||||
|
nand_cleanup(&info->chip);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
clk_disable_unprepare(info->clk);
|
clk_disable_unprepare(info->clk);
|
||||||
|
|
|
@ -1384,10 +1384,12 @@ int denali_init(struct denali_nand_info *denali)
|
||||||
ret = mtd_device_register(mtd, NULL, 0);
|
ret = mtd_device_register(mtd, NULL, 0);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
|
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
|
||||||
goto free_buf;
|
goto cleanup_nand;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
cleanup_nand:
|
||||||
|
nand_cleanup(chip);
|
||||||
free_buf:
|
free_buf:
|
||||||
kfree(denali->buf);
|
kfree(denali->buf);
|
||||||
disable_irq:
|
disable_irq:
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/nand/diskonchip.c
|
|
||||||
*
|
|
||||||
* (C) 2003 Red Hat, Inc.
|
* (C) 2003 Red Hat, Inc.
|
||||||
* (C) 2004 Dan Brown <dan_brown@ieee.org>
|
* (C) 2004 Dan Brown <dan_brown@ieee.org>
|
||||||
* (C) 2004 Kalev Lember <kalev@smartlink.ee>
|
* (C) 2004 Kalev Lember <kalev@smartlink.ee>
|
||||||
|
@ -411,7 +409,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
|
||||||
|
|
||||||
ident.dword = readl(docptr + DoC_2k_CDSN_IO);
|
ident.dword = readl(docptr + DoC_2k_CDSN_IO);
|
||||||
if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
|
if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
|
||||||
printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
|
pr_info("DiskOnChip 2000 responds to DWORD access\n");
|
||||||
this->read_buf = &doc2000_readbuf_dword;
|
this->read_buf = &doc2000_readbuf_dword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -438,7 +436,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
doc->chips_per_floor = i;
|
doc->chips_per_floor = i;
|
||||||
printk(KERN_DEBUG "Detected %d chips per floor.\n", i);
|
pr_debug("Detected %d chips per floor.\n", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)
|
static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)
|
||||||
|
@ -934,14 +932,15 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||||
|
|
||||||
ret = doc_ecc_decode(rs_decoder, dat, calc_ecc);
|
ret = doc_ecc_decode(rs_decoder, dat, calc_ecc);
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
|
pr_err("doc200x_correct_data corrected %d errors\n",
|
||||||
|
ret);
|
||||||
}
|
}
|
||||||
if (DoC_is_MillenniumPlus(doc))
|
if (DoC_is_MillenniumPlus(doc))
|
||||||
WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
|
WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
|
||||||
else
|
else
|
||||||
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
|
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
|
||||||
if (no_ecc_failures && mtd_is_eccerr(ret)) {
|
if (no_ecc_failures && mtd_is_eccerr(ret)) {
|
||||||
printk(KERN_ERR "suppressing ECC failure\n");
|
pr_err("suppressing ECC failure\n");
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1014,11 +1013,11 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch
|
||||||
if (retlen != mtd->writesize)
|
if (retlen != mtd->writesize)
|
||||||
continue;
|
continue;
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs);
|
pr_warn("ECC error scanning DOC at 0x%x\n", offs);
|
||||||
}
|
}
|
||||||
if (memcmp(buf, id, 6))
|
if (memcmp(buf, id, 6))
|
||||||
continue;
|
continue;
|
||||||
printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
|
pr_info("Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
|
||||||
if (doc->mh0_page == -1) {
|
if (doc->mh0_page == -1) {
|
||||||
doc->mh0_page = offs >> this->page_shift;
|
doc->mh0_page = offs >> this->page_shift;
|
||||||
if (!findmirror)
|
if (!findmirror)
|
||||||
|
@ -1029,7 +1028,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (doc->mh0_page == -1) {
|
if (doc->mh0_page == -1) {
|
||||||
printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id);
|
pr_warn("DiskOnChip %s Media Header not found.\n", id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* Only one mediaheader was found. We want buf to contain a
|
/* Only one mediaheader was found. We want buf to contain a
|
||||||
|
@ -1038,7 +1037,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch
|
||||||
ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);
|
ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);
|
||||||
if (retlen != mtd->writesize) {
|
if (retlen != mtd->writesize) {
|
||||||
/* Insanity. Give up. */
|
/* Insanity. Give up. */
|
||||||
printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
|
pr_err("Read DiskOnChip Media Header once, but can't reread it???\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1068,11 +1067,11 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
|
||||||
le16_to_cpus(&mh->FirstPhysicalEUN);
|
le16_to_cpus(&mh->FirstPhysicalEUN);
|
||||||
le32_to_cpus(&mh->FormattedSize);
|
le32_to_cpus(&mh->FormattedSize);
|
||||||
|
|
||||||
printk(KERN_INFO " DataOrgID = %s\n"
|
pr_info(" DataOrgID = %s\n"
|
||||||
" NumEraseUnits = %d\n"
|
" NumEraseUnits = %d\n"
|
||||||
" FirstPhysicalEUN = %d\n"
|
" FirstPhysicalEUN = %d\n"
|
||||||
" FormattedSize = %d\n"
|
" FormattedSize = %d\n"
|
||||||
" UnitSizeFactor = %d\n",
|
" UnitSizeFactor = %d\n",
|
||||||
mh->DataOrgID, mh->NumEraseUnits,
|
mh->DataOrgID, mh->NumEraseUnits,
|
||||||
mh->FirstPhysicalEUN, mh->FormattedSize,
|
mh->FirstPhysicalEUN, mh->FormattedSize,
|
||||||
mh->UnitSizeFactor);
|
mh->UnitSizeFactor);
|
||||||
|
@ -1092,7 +1091,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
|
||||||
maxblocks = min(32768U, (maxblocks << 1) + psize);
|
maxblocks = min(32768U, (maxblocks << 1) + psize);
|
||||||
mh->UnitSizeFactor--;
|
mh->UnitSizeFactor--;
|
||||||
}
|
}
|
||||||
printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
|
pr_warn("UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NOTE: The lines below modify internal variables of the NAND and MTD
|
/* NOTE: The lines below modify internal variables of the NAND and MTD
|
||||||
|
@ -1103,13 +1102,13 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
|
||||||
if (mh->UnitSizeFactor != 0xff) {
|
if (mh->UnitSizeFactor != 0xff) {
|
||||||
this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
|
this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
|
||||||
mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
|
mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
|
||||||
printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize);
|
pr_info("Setting virtual erase size to %d\n", mtd->erasesize);
|
||||||
blocks = mtd->size >> this->bbt_erase_shift;
|
blocks = mtd->size >> this->bbt_erase_shift;
|
||||||
maxblocks = min(32768U, mtd->erasesize - psize);
|
maxblocks = min(32768U, mtd->erasesize - psize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blocks > maxblocks) {
|
if (blocks > maxblocks) {
|
||||||
printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor);
|
pr_err("UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1180,14 +1179,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti
|
||||||
le32_to_cpus(&mh->FormatFlags);
|
le32_to_cpus(&mh->FormatFlags);
|
||||||
le32_to_cpus(&mh->PercentUsed);
|
le32_to_cpus(&mh->PercentUsed);
|
||||||
|
|
||||||
printk(KERN_INFO " bootRecordID = %s\n"
|
pr_info(" bootRecordID = %s\n"
|
||||||
" NoOfBootImageBlocks = %d\n"
|
" NoOfBootImageBlocks = %d\n"
|
||||||
" NoOfBinaryPartitions = %d\n"
|
" NoOfBinaryPartitions = %d\n"
|
||||||
" NoOfBDTLPartitions = %d\n"
|
" NoOfBDTLPartitions = %d\n"
|
||||||
" BlockMultiplerBits = %d\n"
|
" BlockMultiplerBits = %d\n"
|
||||||
" FormatFlgs = %d\n"
|
" FormatFlgs = %d\n"
|
||||||
" OsakVersion = %d.%d.%d.%d\n"
|
" OsakVersion = %d.%d.%d.%d\n"
|
||||||
" PercentUsed = %d\n",
|
" PercentUsed = %d\n",
|
||||||
mh->bootRecordID, mh->NoOfBootImageBlocks,
|
mh->bootRecordID, mh->NoOfBootImageBlocks,
|
||||||
mh->NoOfBinaryPartitions,
|
mh->NoOfBinaryPartitions,
|
||||||
mh->NoOfBDTLPartitions,
|
mh->NoOfBDTLPartitions,
|
||||||
|
@ -1202,13 +1201,13 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti
|
||||||
|
|
||||||
blocks = mtd->size >> vshift;
|
blocks = mtd->size >> vshift;
|
||||||
if (blocks > 32768) {
|
if (blocks > 32768) {
|
||||||
printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits);
|
pr_err("BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
|
blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
|
||||||
if (inftl_bbt_write && (blocks > mtd->erasesize)) {
|
if (inftl_bbt_write && (blocks > mtd->erasesize)) {
|
||||||
printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n");
|
pr_err("Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1222,7 +1221,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti
|
||||||
le32_to_cpus(&ip->spareUnits);
|
le32_to_cpus(&ip->spareUnits);
|
||||||
le32_to_cpus(&ip->Reserved0);
|
le32_to_cpus(&ip->Reserved0);
|
||||||
|
|
||||||
printk(KERN_INFO " PARTITION[%d] ->\n"
|
pr_info(" PARTITION[%d] ->\n"
|
||||||
" virtualUnits = %d\n"
|
" virtualUnits = %d\n"
|
||||||
" firstUnit = %d\n"
|
" firstUnit = %d\n"
|
||||||
" lastUnit = %d\n"
|
" lastUnit = %d\n"
|
||||||
|
@ -1308,7 +1307,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
|
||||||
struct mtd_partition parts[5];
|
struct mtd_partition parts[5];
|
||||||
|
|
||||||
if (this->numchips > doc->chips_per_floor) {
|
if (this->numchips > doc->chips_per_floor) {
|
||||||
printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n");
|
pr_err("Multi-floor INFTL devices not yet supported.\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1436,7 +1435,8 @@ static int __init doc_probe(unsigned long physadr)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
|
virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
|
||||||
if (!virtadr) {
|
if (!virtadr) {
|
||||||
printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr);
|
pr_err("Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n",
|
||||||
|
DOC_IOREMAP_LEN, physadr);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto error_ioremap;
|
goto error_ioremap;
|
||||||
}
|
}
|
||||||
|
@ -1495,7 +1495,7 @@ static int __init doc_probe(unsigned long physadr)
|
||||||
reg = DoC_Mplus_Toggle;
|
reg = DoC_Mplus_Toggle;
|
||||||
break;
|
break;
|
||||||
case DOC_ChipID_DocMilPlus32:
|
case DOC_ChipID_DocMilPlus32:
|
||||||
printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n");
|
pr_err("DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n");
|
||||||
default:
|
default:
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto notfound;
|
goto notfound;
|
||||||
|
@ -1511,7 +1511,7 @@ static int __init doc_probe(unsigned long physadr)
|
||||||
tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
|
tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
|
||||||
tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
|
tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
|
||||||
if ((tmp == tmpb) || (tmp != tmpc)) {
|
if ((tmp == tmpb) || (tmp != tmpc)) {
|
||||||
printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
|
pr_warn("Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto notfound;
|
goto notfound;
|
||||||
}
|
}
|
||||||
|
@ -1545,12 +1545,13 @@ static int __init doc_probe(unsigned long physadr)
|
||||||
}
|
}
|
||||||
newval = ~newval;
|
newval = ~newval;
|
||||||
if (oldval == newval) {
|
if (oldval == newval) {
|
||||||
printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr);
|
pr_debug("Found alias of DOC at 0x%lx to 0x%lx\n",
|
||||||
|
doc->physadr, physadr);
|
||||||
goto notfound;
|
goto notfound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
|
pr_notice("DiskOnChip found at 0x%lx\n", physadr);
|
||||||
|
|
||||||
len = sizeof(struct nand_chip) + sizeof(struct doc_priv) +
|
len = sizeof(struct nand_chip) + sizeof(struct doc_priv) +
|
||||||
(2 * sizeof(struct nand_bbt_descr));
|
(2 * sizeof(struct nand_bbt_descr));
|
||||||
|
@ -1665,12 +1666,13 @@ static int __init init_nanddoc(void)
|
||||||
*/
|
*/
|
||||||
rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
|
rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
|
||||||
if (!rs_decoder) {
|
if (!rs_decoder) {
|
||||||
printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n");
|
pr_err("DiskOnChip: Could not create a RS decoder\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doc_config_location) {
|
if (doc_config_location) {
|
||||||
printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
|
pr_info("Using configured DiskOnChip probe address 0x%lx\n",
|
||||||
|
doc_config_location);
|
||||||
ret = doc_probe(doc_config_location);
|
ret = doc_probe(doc_config_location);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto outerr;
|
goto outerr;
|
||||||
|
@ -1682,7 +1684,7 @@ static int __init init_nanddoc(void)
|
||||||
/* No banner message any more. Print a message if no DiskOnChip
|
/* No banner message any more. Print a message if no DiskOnChip
|
||||||
found, so the user knows we at least tried. */
|
found, so the user knows we at least tried. */
|
||||||
if (!doclist) {
|
if (!doclist) {
|
||||||
printk(KERN_INFO "No valid DiskOnChip devices found\n");
|
pr_info("No valid DiskOnChip devices found\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto outerr;
|
goto outerr;
|
||||||
}
|
}
|
|
@ -1269,8 +1269,8 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
|
||||||
nand->read_buf = docg4_read_buf;
|
nand->read_buf = docg4_read_buf;
|
||||||
nand->write_buf = docg4_write_buf16;
|
nand->write_buf = docg4_write_buf16;
|
||||||
nand->erase = docg4_erase_block;
|
nand->erase = docg4_erase_block;
|
||||||
nand->onfi_set_features = nand_onfi_get_set_features_notsupp;
|
nand->set_features = nand_get_set_features_notsupp;
|
||||||
nand->onfi_get_features = nand_onfi_get_set_features_notsupp;
|
nand->get_features = nand_get_set_features_notsupp;
|
||||||
nand->ecc.read_page = docg4_read_page;
|
nand->ecc.read_page = docg4_read_page;
|
||||||
nand->ecc.write_page = docg4_write_page;
|
nand->ecc.write_page = docg4_write_page;
|
||||||
nand->ecc.read_page_raw = docg4_read_page_raw;
|
nand->ecc.read_page_raw = docg4_read_page_raw;
|
|
@ -775,8 +775,8 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
||||||
chip->select_chip = fsl_elbc_select_chip;
|
chip->select_chip = fsl_elbc_select_chip;
|
||||||
chip->cmdfunc = fsl_elbc_cmdfunc;
|
chip->cmdfunc = fsl_elbc_cmdfunc;
|
||||||
chip->waitfunc = fsl_elbc_wait;
|
chip->waitfunc = fsl_elbc_wait;
|
||||||
chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
|
chip->set_features = nand_get_set_features_notsupp;
|
||||||
chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
|
chip->get_features = nand_get_set_features_notsupp;
|
||||||
|
|
||||||
chip->bbt_td = &bbt_main_descr;
|
chip->bbt_td = &bbt_main_descr;
|
||||||
chip->bbt_md = &bbt_mirror_descr;
|
chip->bbt_md = &bbt_mirror_descr;
|
||||||
|
@ -929,8 +929,8 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
|
||||||
mtd_device_parse_register(mtd, part_probe_types, NULL,
|
mtd_device_parse_register(mtd, part_probe_types, NULL,
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
|
|
||||||
printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n",
|
pr_info("eLBC NAND device at 0x%llx, bank %d\n",
|
||||||
(unsigned long long)res.start, priv->bank);
|
(unsigned long long)res.start, priv->bank);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
|
@ -799,7 +799,7 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
|
||||||
msecs_to_jiffies(IFC_TIMEOUT_MSECS));
|
msecs_to_jiffies(IFC_TIMEOUT_MSECS));
|
||||||
|
|
||||||
if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
|
if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
|
||||||
printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n");
|
pr_err("fsl-ifc: Failed to Initialise SRAM\n");
|
||||||
|
|
||||||
/* Restore CSOR and CSOR_ext */
|
/* Restore CSOR and CSOR_ext */
|
||||||
ifc_out32(csor, &ifc_global->csor_cs[cs].csor);
|
ifc_out32(csor, &ifc_global->csor_cs[cs].csor);
|
||||||
|
@ -832,8 +832,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
|
||||||
chip->select_chip = fsl_ifc_select_chip;
|
chip->select_chip = fsl_ifc_select_chip;
|
||||||
chip->cmdfunc = fsl_ifc_cmdfunc;
|
chip->cmdfunc = fsl_ifc_cmdfunc;
|
||||||
chip->waitfunc = fsl_ifc_wait;
|
chip->waitfunc = fsl_ifc_wait;
|
||||||
chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
|
chip->set_features = nand_get_set_features_notsupp;
|
||||||
chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
|
chip->get_features = nand_get_set_features_notsupp;
|
||||||
|
|
||||||
chip->bbt_td = &bbt_main_descr;
|
chip->bbt_td = &bbt_main_descr;
|
||||||
chip->bbt_md = &bbt_mirror_descr;
|
chip->bbt_md = &bbt_mirror_descr;
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/nand/fsmc_nand.c
|
|
||||||
*
|
|
||||||
* ST Microelectronics
|
* ST Microelectronics
|
||||||
* Flexible Static Memory Controller (FSMC)
|
* Flexible Static Memory Controller (FSMC)
|
||||||
* Driver for NAND portions
|
* Driver for NAND portions
|
||||||
|
@ -9,7 +7,9 @@
|
||||||
* Vipin Kumar <vipin.kumar@st.com>
|
* Vipin Kumar <vipin.kumar@st.com>
|
||||||
* Ashish Priyadarshi
|
* Ashish Priyadarshi
|
||||||
*
|
*
|
||||||
* Based on drivers/mtd/nand/nomadik_nand.c
|
* Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8)
|
||||||
|
* Copyright © 2007 STMicroelectronics Pvt. Ltd.
|
||||||
|
* Copyright © 2009 Alessandro Rubini
|
||||||
*
|
*
|
||||||
* This file is licensed under the terms of the GNU General Public
|
* This file is licensed under the terms of the GNU General Public
|
||||||
* License version 2. This program is licensed "as is" without any
|
* License version 2. This program is licensed "as is" without any
|
||||||
|
@ -103,10 +103,6 @@
|
||||||
#define ECC3 0x1C
|
#define ECC3 0x1C
|
||||||
#define FSMC_NAND_BANK_SZ 0x20
|
#define FSMC_NAND_BANK_SZ 0x20
|
||||||
|
|
||||||
#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \
|
|
||||||
(FSMC_NAND_BANK_SZ * (bank)) + \
|
|
||||||
reg)
|
|
||||||
|
|
||||||
#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
|
#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
|
||||||
|
|
||||||
struct fsmc_nand_timings {
|
struct fsmc_nand_timings {
|
||||||
|
@ -143,7 +139,7 @@ enum access_mode {
|
||||||
* @data_va: NAND port for Data.
|
* @data_va: NAND port for Data.
|
||||||
* @cmd_va: NAND port for Command.
|
* @cmd_va: NAND port for Command.
|
||||||
* @addr_va: NAND port for Address.
|
* @addr_va: NAND port for Address.
|
||||||
* @regs_va: FSMC regs base address.
|
* @regs_va: Registers base address for a given bank.
|
||||||
*/
|
*/
|
||||||
struct fsmc_nand_data {
|
struct fsmc_nand_data {
|
||||||
u32 pid;
|
u32 pid;
|
||||||
|
@ -257,45 +253,6 @@ static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd)
|
||||||
return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand);
|
return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* fsmc_cmd_ctrl - For facilitaing Hardware access
|
|
||||||
* This routine allows hardware specific access to control-lines(ALE,CLE)
|
|
||||||
*/
|
|
||||||
static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
|
||||||
{
|
|
||||||
struct nand_chip *this = mtd_to_nand(mtd);
|
|
||||||
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
|
||||||
void __iomem *regs = host->regs_va;
|
|
||||||
unsigned int bank = host->bank;
|
|
||||||
|
|
||||||
if (ctrl & NAND_CTRL_CHANGE) {
|
|
||||||
u32 pc;
|
|
||||||
|
|
||||||
if (ctrl & NAND_CLE) {
|
|
||||||
this->IO_ADDR_R = host->cmd_va;
|
|
||||||
this->IO_ADDR_W = host->cmd_va;
|
|
||||||
} else if (ctrl & NAND_ALE) {
|
|
||||||
this->IO_ADDR_R = host->addr_va;
|
|
||||||
this->IO_ADDR_W = host->addr_va;
|
|
||||||
} else {
|
|
||||||
this->IO_ADDR_R = host->data_va;
|
|
||||||
this->IO_ADDR_W = host->data_va;
|
|
||||||
}
|
|
||||||
|
|
||||||
pc = readl(FSMC_NAND_REG(regs, bank, PC));
|
|
||||||
if (ctrl & NAND_NCE)
|
|
||||||
pc |= FSMC_ENABLE;
|
|
||||||
else
|
|
||||||
pc &= ~FSMC_ENABLE;
|
|
||||||
writel_relaxed(pc, FSMC_NAND_REG(regs, bank, PC));
|
|
||||||
}
|
|
||||||
|
|
||||||
mb();
|
|
||||||
|
|
||||||
if (cmd != NAND_CMD_NONE)
|
|
||||||
writeb_relaxed(cmd, this->IO_ADDR_W);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine
|
* fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine
|
||||||
*
|
*
|
||||||
|
@ -307,8 +264,6 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host,
|
||||||
{
|
{
|
||||||
uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
|
uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
|
||||||
uint32_t tclr, tar, thiz, thold, twait, tset;
|
uint32_t tclr, tar, thiz, thold, twait, tset;
|
||||||
unsigned int bank = host->bank;
|
|
||||||
void __iomem *regs = host->regs_va;
|
|
||||||
|
|
||||||
tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
|
tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
|
||||||
tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
|
tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
|
||||||
|
@ -318,18 +273,14 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host,
|
||||||
tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
|
tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
|
||||||
|
|
||||||
if (host->nand.options & NAND_BUSWIDTH_16)
|
if (host->nand.options & NAND_BUSWIDTH_16)
|
||||||
writel_relaxed(value | FSMC_DEVWID_16,
|
writel_relaxed(value | FSMC_DEVWID_16, host->regs_va + PC);
|
||||||
FSMC_NAND_REG(regs, bank, PC));
|
|
||||||
else
|
else
|
||||||
writel_relaxed(value | FSMC_DEVWID_8,
|
writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + PC);
|
||||||
FSMC_NAND_REG(regs, bank, PC));
|
|
||||||
|
|
||||||
writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar,
|
writel_relaxed(readl(host->regs_va + PC) | tclr | tar,
|
||||||
FSMC_NAND_REG(regs, bank, PC));
|
host->regs_va + PC);
|
||||||
writel_relaxed(thiz | thold | twait | tset,
|
writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM);
|
||||||
FSMC_NAND_REG(regs, bank, COMM));
|
writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB);
|
||||||
writel_relaxed(thiz | thold | twait | tset,
|
|
||||||
FSMC_NAND_REG(regs, bank, ATTRIB));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsmc_calc_timings(struct fsmc_nand_data *host,
|
static int fsmc_calc_timings(struct fsmc_nand_data *host,
|
||||||
|
@ -419,15 +370,13 @@ static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline,
|
||||||
static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
|
static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||||
{
|
{
|
||||||
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||||
void __iomem *regs = host->regs_va;
|
|
||||||
uint32_t bank = host->bank;
|
|
||||||
|
|
||||||
writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256,
|
writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCPLEN_256,
|
||||||
FSMC_NAND_REG(regs, bank, PC));
|
host->regs_va + PC);
|
||||||
writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN,
|
writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCEN,
|
||||||
FSMC_NAND_REG(regs, bank, PC));
|
host->regs_va + PC);
|
||||||
writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN,
|
writel_relaxed(readl(host->regs_va + PC) | FSMC_ECCEN,
|
||||||
FSMC_NAND_REG(regs, bank, PC));
|
host->regs_va + PC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -439,13 +388,11 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
|
||||||
uint8_t *ecc)
|
uint8_t *ecc)
|
||||||
{
|
{
|
||||||
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||||
void __iomem *regs = host->regs_va;
|
|
||||||
uint32_t bank = host->bank;
|
|
||||||
uint32_t ecc_tmp;
|
uint32_t ecc_tmp;
|
||||||
unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
|
unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY)
|
if (readl_relaxed(host->regs_va + STS) & FSMC_CODE_RDY)
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
@ -456,25 +403,25 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1));
|
ecc_tmp = readl_relaxed(host->regs_va + ECC1);
|
||||||
ecc[0] = (uint8_t) (ecc_tmp >> 0);
|
ecc[0] = (uint8_t) (ecc_tmp >> 0);
|
||||||
ecc[1] = (uint8_t) (ecc_tmp >> 8);
|
ecc[1] = (uint8_t) (ecc_tmp >> 8);
|
||||||
ecc[2] = (uint8_t) (ecc_tmp >> 16);
|
ecc[2] = (uint8_t) (ecc_tmp >> 16);
|
||||||
ecc[3] = (uint8_t) (ecc_tmp >> 24);
|
ecc[3] = (uint8_t) (ecc_tmp >> 24);
|
||||||
|
|
||||||
ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2));
|
ecc_tmp = readl_relaxed(host->regs_va + ECC2);
|
||||||
ecc[4] = (uint8_t) (ecc_tmp >> 0);
|
ecc[4] = (uint8_t) (ecc_tmp >> 0);
|
||||||
ecc[5] = (uint8_t) (ecc_tmp >> 8);
|
ecc[5] = (uint8_t) (ecc_tmp >> 8);
|
||||||
ecc[6] = (uint8_t) (ecc_tmp >> 16);
|
ecc[6] = (uint8_t) (ecc_tmp >> 16);
|
||||||
ecc[7] = (uint8_t) (ecc_tmp >> 24);
|
ecc[7] = (uint8_t) (ecc_tmp >> 24);
|
||||||
|
|
||||||
ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3));
|
ecc_tmp = readl_relaxed(host->regs_va + ECC3);
|
||||||
ecc[8] = (uint8_t) (ecc_tmp >> 0);
|
ecc[8] = (uint8_t) (ecc_tmp >> 0);
|
||||||
ecc[9] = (uint8_t) (ecc_tmp >> 8);
|
ecc[9] = (uint8_t) (ecc_tmp >> 8);
|
||||||
ecc[10] = (uint8_t) (ecc_tmp >> 16);
|
ecc[10] = (uint8_t) (ecc_tmp >> 16);
|
||||||
ecc[11] = (uint8_t) (ecc_tmp >> 24);
|
ecc[11] = (uint8_t) (ecc_tmp >> 24);
|
||||||
|
|
||||||
ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, STS));
|
ecc_tmp = readl_relaxed(host->regs_va + STS);
|
||||||
ecc[12] = (uint8_t) (ecc_tmp >> 16);
|
ecc[12] = (uint8_t) (ecc_tmp >> 16);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -489,11 +436,9 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
|
||||||
uint8_t *ecc)
|
uint8_t *ecc)
|
||||||
{
|
{
|
||||||
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||||
void __iomem *regs = host->regs_va;
|
|
||||||
uint32_t bank = host->bank;
|
|
||||||
uint32_t ecc_tmp;
|
uint32_t ecc_tmp;
|
||||||
|
|
||||||
ecc_tmp = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1));
|
ecc_tmp = readl_relaxed(host->regs_va + ECC1);
|
||||||
ecc[0] = (uint8_t) (ecc_tmp >> 0);
|
ecc[0] = (uint8_t) (ecc_tmp >> 0);
|
||||||
ecc[1] = (uint8_t) (ecc_tmp >> 8);
|
ecc[1] = (uint8_t) (ecc_tmp >> 8);
|
||||||
ecc[2] = (uint8_t) (ecc_tmp >> 16);
|
ecc[2] = (uint8_t) (ecc_tmp >> 16);
|
||||||
|
@ -598,18 +543,18 @@ unmap_dma:
|
||||||
*/
|
*/
|
||||||
static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
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;
|
int i;
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
||||||
|
|
||||||
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
|
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
|
||||||
IS_ALIGNED(len, sizeof(uint32_t))) {
|
IS_ALIGNED(len, sizeof(uint32_t))) {
|
||||||
uint32_t *p = (uint32_t *)buf;
|
uint32_t *p = (uint32_t *)buf;
|
||||||
len = len >> 2;
|
len = len >> 2;
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
writel_relaxed(p[i], chip->IO_ADDR_W);
|
writel_relaxed(p[i], host->data_va);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
writeb_relaxed(buf[i], chip->IO_ADDR_W);
|
writeb_relaxed(buf[i], host->data_va);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,18 +566,18 @@ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||||
*/
|
*/
|
||||||
static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
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;
|
int i;
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
||||||
|
|
||||||
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
|
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
|
||||||
IS_ALIGNED(len, sizeof(uint32_t))) {
|
IS_ALIGNED(len, sizeof(uint32_t))) {
|
||||||
uint32_t *p = (uint32_t *)buf;
|
uint32_t *p = (uint32_t *)buf;
|
||||||
len = len >> 2;
|
len = len >> 2;
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
p[i] = readl_relaxed(chip->IO_ADDR_R);
|
p[i] = readl_relaxed(host->data_va);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
buf[i] = readb_relaxed(chip->IO_ADDR_R);
|
buf[i] = readb_relaxed(host->data_va);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,6 +608,102 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
|
||||||
dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE);
|
dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* fsmc_select_chip - assert or deassert nCE */
|
||||||
|
static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
|
||||||
|
{
|
||||||
|
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||||
|
u32 pc;
|
||||||
|
|
||||||
|
/* Support only one CS */
|
||||||
|
if (chipnr > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pc = readl(host->regs_va + PC);
|
||||||
|
if (chipnr < 0)
|
||||||
|
writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + PC);
|
||||||
|
else
|
||||||
|
writel_relaxed(pc | FSMC_ENABLE, host->regs_va + PC);
|
||||||
|
|
||||||
|
/* nCE line must be asserted before starting any operation */
|
||||||
|
mb();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fsmc_exec_op - hook called by the core to execute NAND operations
|
||||||
|
*
|
||||||
|
* This controller is simple enough and thus does not need to use the parser
|
||||||
|
* provided by the core, instead, handle every situation here.
|
||||||
|
*/
|
||||||
|
static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
|
||||||
|
bool check_only)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||||
|
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||||
|
const struct nand_op_instr *instr = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
unsigned int op_id;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pr_debug("Executing operation [%d instructions]:\n", op->ninstrs);
|
||||||
|
for (op_id = 0; op_id < op->ninstrs; op_id++) {
|
||||||
|
instr = &op->instrs[op_id];
|
||||||
|
|
||||||
|
switch (instr->type) {
|
||||||
|
case NAND_OP_CMD_INSTR:
|
||||||
|
pr_debug(" ->CMD [0x%02x]\n",
|
||||||
|
instr->ctx.cmd.opcode);
|
||||||
|
|
||||||
|
writeb_relaxed(instr->ctx.cmd.opcode, host->cmd_va);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_OP_ADDR_INSTR:
|
||||||
|
pr_debug(" ->ADDR [%d cyc]",
|
||||||
|
instr->ctx.addr.naddrs);
|
||||||
|
|
||||||
|
for (i = 0; i < instr->ctx.addr.naddrs; i++)
|
||||||
|
writeb_relaxed(instr->ctx.addr.addrs[i],
|
||||||
|
host->addr_va);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_OP_DATA_IN_INSTR:
|
||||||
|
pr_debug(" ->DATA_IN [%d B%s]\n", instr->ctx.data.len,
|
||||||
|
instr->ctx.data.force_8bit ?
|
||||||
|
", force 8-bit" : "");
|
||||||
|
|
||||||
|
if (host->mode == USE_DMA_ACCESS)
|
||||||
|
fsmc_read_buf_dma(mtd, instr->ctx.data.buf.in,
|
||||||
|
instr->ctx.data.len);
|
||||||
|
else
|
||||||
|
fsmc_read_buf(mtd, instr->ctx.data.buf.in,
|
||||||
|
instr->ctx.data.len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_OP_DATA_OUT_INSTR:
|
||||||
|
pr_debug(" ->DATA_OUT [%d B%s]\n", instr->ctx.data.len,
|
||||||
|
instr->ctx.data.force_8bit ?
|
||||||
|
", force 8-bit" : "");
|
||||||
|
|
||||||
|
if (host->mode == USE_DMA_ACCESS)
|
||||||
|
fsmc_write_buf_dma(mtd, instr->ctx.data.buf.out,
|
||||||
|
instr->ctx.data.len);
|
||||||
|
else
|
||||||
|
fsmc_write_buf(mtd, instr->ctx.data.buf.out,
|
||||||
|
instr->ctx.data.len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAND_OP_WAITRDY_INSTR:
|
||||||
|
pr_debug(" ->WAITRDY [max %d ms]\n",
|
||||||
|
instr->ctx.waitrdy.timeout_ms);
|
||||||
|
|
||||||
|
ret = nand_soft_waitrdy(chip,
|
||||||
|
instr->ctx.waitrdy.timeout_ms);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fsmc_read_page_hwecc
|
* fsmc_read_page_hwecc
|
||||||
* @mtd: mtd info structure
|
* @mtd: mtd info structure
|
||||||
|
@ -754,13 +795,11 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat,
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
|
||||||
void __iomem *regs = host->regs_va;
|
|
||||||
unsigned int bank = host->bank;
|
|
||||||
uint32_t err_idx[8];
|
uint32_t err_idx[8];
|
||||||
uint32_t num_err, i;
|
uint32_t num_err, i;
|
||||||
uint32_t ecc1, ecc2, ecc3, ecc4;
|
uint32_t ecc1, ecc2, ecc3, ecc4;
|
||||||
|
|
||||||
num_err = (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF;
|
num_err = (readl_relaxed(host->regs_va + STS) >> 10) & 0xF;
|
||||||
|
|
||||||
/* no bit flipping */
|
/* no bit flipping */
|
||||||
if (likely(num_err == 0))
|
if (likely(num_err == 0))
|
||||||
|
@ -803,10 +842,10 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat,
|
||||||
* uint64_t array and error offset indexes are populated in err_idx
|
* uint64_t array and error offset indexes are populated in err_idx
|
||||||
* array
|
* array
|
||||||
*/
|
*/
|
||||||
ecc1 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1));
|
ecc1 = readl_relaxed(host->regs_va + ECC1);
|
||||||
ecc2 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2));
|
ecc2 = readl_relaxed(host->regs_va + ECC2);
|
||||||
ecc3 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3));
|
ecc3 = readl_relaxed(host->regs_va + ECC3);
|
||||||
ecc4 = readl_relaxed(FSMC_NAND_REG(regs, bank, STS));
|
ecc4 = readl_relaxed(host->regs_va + STS);
|
||||||
|
|
||||||
err_idx[0] = (ecc1 >> 0) & 0x1FFF;
|
err_idx[0] = (ecc1 >> 0) & 0x1FFF;
|
||||||
err_idx[1] = (ecc1 >> 13) & 0x1FFF;
|
err_idx[1] = (ecc1 >> 13) & 0x1FFF;
|
||||||
|
@ -889,6 +928,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
struct nand_chip *nand;
|
struct nand_chip *nand;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
void __iomem *base;
|
||||||
dma_cap_mask_t mask;
|
dma_cap_mask_t mask;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
|
@ -923,9 +963,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
return PTR_ERR(host->cmd_va);
|
return PTR_ERR(host->cmd_va);
|
||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
|
||||||
host->regs_va = devm_ioremap_resource(&pdev->dev, res);
|
base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(host->regs_va))
|
if (IS_ERR(base))
|
||||||
return PTR_ERR(host->regs_va);
|
return PTR_ERR(base);
|
||||||
|
|
||||||
|
host->regs_va = base + FSMC_NOR_REG_SIZE +
|
||||||
|
(host->bank * FSMC_NAND_BANK_SZ);
|
||||||
|
|
||||||
host->clk = devm_clk_get(&pdev->dev, NULL);
|
host->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(host->clk)) {
|
if (IS_ERR(host->clk)) {
|
||||||
|
@ -942,7 +985,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
* AMBA PrimeCell bus. However it is not a PrimeCell.
|
* AMBA PrimeCell bus. However it is not a PrimeCell.
|
||||||
*/
|
*/
|
||||||
for (pid = 0, i = 0; i < 4; i++)
|
for (pid = 0, i = 0; i < 4; i++)
|
||||||
pid |= (readl(host->regs_va + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8);
|
pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8);
|
||||||
host->pid = pid;
|
host->pid = pid;
|
||||||
dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, "
|
dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, "
|
||||||
"revision %02x, config %02x\n",
|
"revision %02x, config %02x\n",
|
||||||
|
@ -960,9 +1003,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
nand_set_flash_node(nand, pdev->dev.of_node);
|
nand_set_flash_node(nand, pdev->dev.of_node);
|
||||||
|
|
||||||
mtd->dev.parent = &pdev->dev;
|
mtd->dev.parent = &pdev->dev;
|
||||||
nand->IO_ADDR_R = host->data_va;
|
nand->exec_op = fsmc_exec_op;
|
||||||
nand->IO_ADDR_W = host->data_va;
|
nand->select_chip = fsmc_select_chip;
|
||||||
nand->cmd_ctrl = fsmc_cmd_ctrl;
|
|
||||||
nand->chip_delay = 30;
|
nand->chip_delay = 30;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -974,8 +1016,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
nand->ecc.size = 512;
|
nand->ecc.size = 512;
|
||||||
nand->badblockbits = 7;
|
nand->badblockbits = 7;
|
||||||
|
|
||||||
switch (host->mode) {
|
if (host->mode == USE_DMA_ACCESS) {
|
||||||
case USE_DMA_ACCESS:
|
|
||||||
dma_cap_zero(mask);
|
dma_cap_zero(mask);
|
||||||
dma_cap_set(DMA_MEMCPY, mask);
|
dma_cap_set(DMA_MEMCPY, mask);
|
||||||
host->read_dma_chan = dma_request_channel(mask, filter, NULL);
|
host->read_dma_chan = dma_request_channel(mask, filter, NULL);
|
||||||
|
@ -988,15 +1029,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||||
dev_err(&pdev->dev, "Unable to get write dma channel\n");
|
dev_err(&pdev->dev, "Unable to get write dma channel\n");
|
||||||
goto err_req_write_chnl;
|
goto err_req_write_chnl;
|
||||||
}
|
}
|
||||||
nand->read_buf = fsmc_read_buf_dma;
|
|
||||||
nand->write_buf = fsmc_write_buf_dma;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
case USE_WORD_ACCESS:
|
|
||||||
nand->read_buf = fsmc_read_buf;
|
|
||||||
nand->write_buf = fsmc_write_buf;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host->dev_timings)
|
if (host->dev_timings)
|
|
@ -1,6 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* drivers/mtd/nand/gpio.c
|
|
||||||
*
|
|
||||||
* Updated, and converted to generic GPIO based driver by Russell King.
|
* Updated, and converted to generic GPIO based driver by Russell King.
|
||||||
*
|
*
|
||||||
* Written by Ben Dooks <ben@simtec.co.uk>
|
* Written by Ben Dooks <ben@simtec.co.uk>
|
|
@ -26,15 +26,8 @@
|
||||||
#include "gpmi-regs.h"
|
#include "gpmi-regs.h"
|
||||||
#include "bch-regs.h"
|
#include "bch-regs.h"
|
||||||
|
|
||||||
static struct timing_threshold timing_default_threshold = {
|
/* Converts time to clock cycles */
|
||||||
.max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
|
#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period)
|
||||||
BP_GPMI_TIMING0_DATA_SETUP),
|
|
||||||
.internal_data_setup_in_ns = 0,
|
|
||||||
.max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >>
|
|
||||||
BP_GPMI_CTRL1_RDN_DELAY),
|
|
||||||
.max_dll_clock_period_in_ns = 32,
|
|
||||||
.max_dll_delay_in_ns = 16,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MXS_SET_ADDR 0x4
|
#define MXS_SET_ADDR 0x4
|
||||||
#define MXS_CLR_ADDR 0x8
|
#define MXS_CLR_ADDR 0x8
|
||||||
|
@ -151,8 +144,15 @@ err_clk:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true)
|
int gpmi_enable_clk(struct gpmi_nand_data *this)
|
||||||
#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false)
|
{
|
||||||
|
return __gpmi_enable_clk(this, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int gpmi_disable_clk(struct gpmi_nand_data *this)
|
||||||
|
{
|
||||||
|
return __gpmi_enable_clk(this, false);
|
||||||
|
}
|
||||||
|
|
||||||
int gpmi_init(struct gpmi_nand_data *this)
|
int gpmi_init(struct gpmi_nand_data *this)
|
||||||
{
|
{
|
||||||
|
@ -174,7 +174,6 @@ int gpmi_init(struct gpmi_nand_data *this)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
||||||
|
|
||||||
/* Choose NAND mode. */
|
/* Choose NAND mode. */
|
||||||
writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR);
|
writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR);
|
||||||
|
|
||||||
|
@ -313,467 +312,6 @@ err_out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Converts time in nanoseconds to cycles. */
|
|
||||||
static unsigned int ns_to_cycles(unsigned int time,
|
|
||||||
unsigned int period, unsigned int min)
|
|
||||||
{
|
|
||||||
unsigned int k;
|
|
||||||
|
|
||||||
k = (time + period - 1) / period;
|
|
||||||
return max(k, min);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DEF_MIN_PROP_DELAY 5
|
|
||||||
#define DEF_MAX_PROP_DELAY 9
|
|
||||||
/* Apply timing to current hardware conditions. */
|
|
||||||
static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
|
|
||||||
struct gpmi_nfc_hardware_timing *hw)
|
|
||||||
{
|
|
||||||
struct timing_threshold *nfc = &timing_default_threshold;
|
|
||||||
struct resources *r = &this->resources;
|
|
||||||
struct nand_chip *nand = &this->nand;
|
|
||||||
struct nand_timing target = this->timing;
|
|
||||||
bool improved_timing_is_available;
|
|
||||||
unsigned long clock_frequency_in_hz;
|
|
||||||
unsigned int clock_period_in_ns;
|
|
||||||
bool dll_use_half_periods;
|
|
||||||
unsigned int dll_delay_shift;
|
|
||||||
unsigned int max_sample_delay_in_ns;
|
|
||||||
unsigned int address_setup_in_cycles;
|
|
||||||
unsigned int data_setup_in_ns;
|
|
||||||
unsigned int data_setup_in_cycles;
|
|
||||||
unsigned int data_hold_in_cycles;
|
|
||||||
int ideal_sample_delay_in_ns;
|
|
||||||
unsigned int sample_delay_factor;
|
|
||||||
int tEYE;
|
|
||||||
unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY;
|
|
||||||
unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there are multiple chips, we need to relax the timings to allow
|
|
||||||
* for signal distortion due to higher capacitance.
|
|
||||||
*/
|
|
||||||
if (nand->numchips > 2) {
|
|
||||||
target.data_setup_in_ns += 10;
|
|
||||||
target.data_hold_in_ns += 10;
|
|
||||||
target.address_setup_in_ns += 10;
|
|
||||||
} else if (nand->numchips > 1) {
|
|
||||||
target.data_setup_in_ns += 5;
|
|
||||||
target.data_hold_in_ns += 5;
|
|
||||||
target.address_setup_in_ns += 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if improved timing information is available. */
|
|
||||||
improved_timing_is_available =
|
|
||||||
(target.tREA_in_ns >= 0) &&
|
|
||||||
(target.tRLOH_in_ns >= 0) &&
|
|
||||||
(target.tRHOH_in_ns >= 0);
|
|
||||||
|
|
||||||
/* Inspect the clock. */
|
|
||||||
nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]);
|
|
||||||
clock_frequency_in_hz = nfc->clock_frequency_in_hz;
|
|
||||||
clock_period_in_ns = NSEC_PER_SEC / clock_frequency_in_hz;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The NFC quantizes setup and hold parameters in terms of clock cycles.
|
|
||||||
* Here, we quantize the setup and hold timing parameters to the
|
|
||||||
* next-highest clock period to make sure we apply at least the
|
|
||||||
* specified times.
|
|
||||||
*
|
|
||||||
* For data setup and data hold, the hardware interprets a value of zero
|
|
||||||
* as the largest possible delay. This is not what's intended by a zero
|
|
||||||
* in the input parameter, so we impose a minimum of one cycle.
|
|
||||||
*/
|
|
||||||
data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns,
|
|
||||||
clock_period_in_ns, 1);
|
|
||||||
data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns,
|
|
||||||
clock_period_in_ns, 1);
|
|
||||||
address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns,
|
|
||||||
clock_period_in_ns, 0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The clock's period affects the sample delay in a number of ways:
|
|
||||||
*
|
|
||||||
* (1) The NFC HAL tells us the maximum clock period the sample delay
|
|
||||||
* DLL can tolerate. If the clock period is greater than half that
|
|
||||||
* maximum, we must configure the DLL to be driven by half periods.
|
|
||||||
*
|
|
||||||
* (2) We need to convert from an ideal sample delay, in ns, to a
|
|
||||||
* "sample delay factor," which the NFC uses. This factor depends on
|
|
||||||
* whether we're driving the DLL with full or half periods.
|
|
||||||
* Paraphrasing the reference manual:
|
|
||||||
*
|
|
||||||
* AD = SDF x 0.125 x RP
|
|
||||||
*
|
|
||||||
* where:
|
|
||||||
*
|
|
||||||
* AD is the applied delay, in ns.
|
|
||||||
* SDF is the sample delay factor, which is dimensionless.
|
|
||||||
* RP is the reference period, in ns, which is a full clock period
|
|
||||||
* if the DLL is being driven by full periods, or half that if
|
|
||||||
* the DLL is being driven by half periods.
|
|
||||||
*
|
|
||||||
* Let's re-arrange this in a way that's more useful to us:
|
|
||||||
*
|
|
||||||
* 8
|
|
||||||
* SDF = AD x ----
|
|
||||||
* RP
|
|
||||||
*
|
|
||||||
* The reference period is either the clock period or half that, so this
|
|
||||||
* is:
|
|
||||||
*
|
|
||||||
* 8 AD x DDF
|
|
||||||
* SDF = AD x ----- = --------
|
|
||||||
* f x P P
|
|
||||||
*
|
|
||||||
* where:
|
|
||||||
*
|
|
||||||
* f is 1 or 1/2, depending on how we're driving the DLL.
|
|
||||||
* P is the clock period.
|
|
||||||
* DDF is the DLL Delay Factor, a dimensionless value that
|
|
||||||
* incorporates all the constants in the conversion.
|
|
||||||
*
|
|
||||||
* DDF will be either 8 or 16, both of which are powers of two. We can
|
|
||||||
* reduce the cost of this conversion by using bit shifts instead of
|
|
||||||
* multiplication or division. Thus:
|
|
||||||
*
|
|
||||||
* AD << DDS
|
|
||||||
* SDF = ---------
|
|
||||||
* P
|
|
||||||
*
|
|
||||||
* or
|
|
||||||
*
|
|
||||||
* AD = (SDF >> DDS) x P
|
|
||||||
*
|
|
||||||
* where:
|
|
||||||
*
|
|
||||||
* DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF.
|
|
||||||
*/
|
|
||||||
if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) {
|
|
||||||
dll_use_half_periods = true;
|
|
||||||
dll_delay_shift = 3 + 1;
|
|
||||||
} else {
|
|
||||||
dll_use_half_periods = false;
|
|
||||||
dll_delay_shift = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compute the maximum sample delay the NFC allows, under current
|
|
||||||
* conditions. If the clock is running too slowly, no sample delay is
|
|
||||||
* possible.
|
|
||||||
*/
|
|
||||||
if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns)
|
|
||||||
max_sample_delay_in_ns = 0;
|
|
||||||
else {
|
|
||||||
/*
|
|
||||||
* Compute the delay implied by the largest sample delay factor
|
|
||||||
* the NFC allows.
|
|
||||||
*/
|
|
||||||
max_sample_delay_in_ns =
|
|
||||||
(nfc->max_sample_delay_factor * clock_period_in_ns) >>
|
|
||||||
dll_delay_shift;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if the implied sample delay larger than the NFC
|
|
||||||
* actually allows.
|
|
||||||
*/
|
|
||||||
if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns)
|
|
||||||
max_sample_delay_in_ns = nfc->max_dll_delay_in_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if improved timing information is available. If not, we have to
|
|
||||||
* use a less-sophisticated algorithm.
|
|
||||||
*/
|
|
||||||
if (!improved_timing_is_available) {
|
|
||||||
/*
|
|
||||||
* Fold the read setup time required by the NFC into the ideal
|
|
||||||
* sample delay.
|
|
||||||
*/
|
|
||||||
ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns +
|
|
||||||
nfc->internal_data_setup_in_ns;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The ideal sample delay may be greater than the maximum
|
|
||||||
* allowed by the NFC. If so, we can trade off sample delay time
|
|
||||||
* for more data setup time.
|
|
||||||
*
|
|
||||||
* In each iteration of the following loop, we add a cycle to
|
|
||||||
* the data setup time and subtract a corresponding amount from
|
|
||||||
* the sample delay until we've satisified the constraints or
|
|
||||||
* can't do any better.
|
|
||||||
*/
|
|
||||||
while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) &&
|
|
||||||
(data_setup_in_cycles < nfc->max_data_setup_cycles)) {
|
|
||||||
|
|
||||||
data_setup_in_cycles++;
|
|
||||||
ideal_sample_delay_in_ns -= clock_period_in_ns;
|
|
||||||
|
|
||||||
if (ideal_sample_delay_in_ns < 0)
|
|
||||||
ideal_sample_delay_in_ns = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compute the sample delay factor that corresponds most closely
|
|
||||||
* to the ideal sample delay. If the result is too large for the
|
|
||||||
* NFC, use the maximum value.
|
|
||||||
*
|
|
||||||
* Notice that we use the ns_to_cycles function to compute the
|
|
||||||
* sample delay factor. We do this because the form of the
|
|
||||||
* computation is the same as that for calculating cycles.
|
|
||||||
*/
|
|
||||||
sample_delay_factor =
|
|
||||||
ns_to_cycles(
|
|
||||||
ideal_sample_delay_in_ns << dll_delay_shift,
|
|
||||||
clock_period_in_ns, 0);
|
|
||||||
|
|
||||||
if (sample_delay_factor > nfc->max_sample_delay_factor)
|
|
||||||
sample_delay_factor = nfc->max_sample_delay_factor;
|
|
||||||
|
|
||||||
/* Skip to the part where we return our results. */
|
|
||||||
goto return_results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If control arrives here, we have more detailed timing information,
|
|
||||||
* so we can use a better algorithm.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fold the read setup time required by the NFC into the maximum
|
|
||||||
* propagation delay.
|
|
||||||
*/
|
|
||||||
max_prop_delay_in_ns += nfc->internal_data_setup_in_ns;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Earlier, we computed the number of clock cycles required to satisfy
|
|
||||||
* the data setup time. Now, we need to know the actual nanoseconds.
|
|
||||||
*/
|
|
||||||
data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compute tEYE, the width of the data eye when reading from the NAND
|
|
||||||
* Flash. The eye width is fundamentally determined by the data setup
|
|
||||||
* time, perturbed by propagation delays and some characteristics of the
|
|
||||||
* NAND Flash device.
|
|
||||||
*
|
|
||||||
* start of the eye = max_prop_delay + tREA
|
|
||||||
* end of the eye = min_prop_delay + tRHOH + data_setup
|
|
||||||
*/
|
|
||||||
tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns +
|
|
||||||
(int)data_setup_in_ns;
|
|
||||||
|
|
||||||
tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The eye must be open. If it's not, we can try to open it by
|
|
||||||
* increasing its main forcer, the data setup time.
|
|
||||||
*
|
|
||||||
* In each iteration of the following loop, we increase the data setup
|
|
||||||
* time by a single clock cycle. We do this until either the eye is
|
|
||||||
* open or we run into NFC limits.
|
|
||||||
*/
|
|
||||||
while ((tEYE <= 0) &&
|
|
||||||
(data_setup_in_cycles < nfc->max_data_setup_cycles)) {
|
|
||||||
/* Give a cycle to data setup. */
|
|
||||||
data_setup_in_cycles++;
|
|
||||||
/* Synchronize the data setup time with the cycles. */
|
|
||||||
data_setup_in_ns += clock_period_in_ns;
|
|
||||||
/* Adjust tEYE accordingly. */
|
|
||||||
tEYE += clock_period_in_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When control arrives here, the eye is open. The ideal time to sample
|
|
||||||
* the data is in the center of the eye:
|
|
||||||
*
|
|
||||||
* end of the eye + start of the eye
|
|
||||||
* --------------------------------- - data_setup
|
|
||||||
* 2
|
|
||||||
*
|
|
||||||
* After some algebra, this simplifies to the code immediately below.
|
|
||||||
*/
|
|
||||||
ideal_sample_delay_in_ns =
|
|
||||||
((int)max_prop_delay_in_ns +
|
|
||||||
(int)target.tREA_in_ns +
|
|
||||||
(int)min_prop_delay_in_ns +
|
|
||||||
(int)target.tRHOH_in_ns -
|
|
||||||
(int)data_setup_in_ns) >> 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following figure illustrates some aspects of a NAND Flash read:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* __ _____________________________________
|
|
||||||
* RDN \_________________/
|
|
||||||
*
|
|
||||||
* <---- tEYE ----->
|
|
||||||
* /-----------------\
|
|
||||||
* Read Data ----------------------------< >---------
|
|
||||||
* \-----------------/
|
|
||||||
* ^ ^ ^ ^
|
|
||||||
* | | | |
|
|
||||||
* |<--Data Setup -->|<--Delay Time -->| |
|
|
||||||
* | | | |
|
|
||||||
* | | |
|
|
||||||
* | |<-- Quantized Delay Time -->|
|
|
||||||
* | | |
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* We have some issues we must now address:
|
|
||||||
*
|
|
||||||
* (1) The *ideal* sample delay time must not be negative. If it is, we
|
|
||||||
* jam it to zero.
|
|
||||||
*
|
|
||||||
* (2) The *ideal* sample delay time must not be greater than that
|
|
||||||
* allowed by the NFC. If it is, we can increase the data setup
|
|
||||||
* time, which will reduce the delay between the end of the data
|
|
||||||
* setup and the center of the eye. It will also make the eye
|
|
||||||
* larger, which might help with the next issue...
|
|
||||||
*
|
|
||||||
* (3) The *quantized* sample delay time must not fall either before the
|
|
||||||
* eye opens or after it closes (the latter is the problem
|
|
||||||
* illustrated in the above figure).
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Jam a negative ideal sample delay to zero. */
|
|
||||||
if (ideal_sample_delay_in_ns < 0)
|
|
||||||
ideal_sample_delay_in_ns = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Extend the data setup as needed to reduce the ideal sample delay
|
|
||||||
* below the maximum permitted by the NFC.
|
|
||||||
*/
|
|
||||||
while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) &&
|
|
||||||
(data_setup_in_cycles < nfc->max_data_setup_cycles)) {
|
|
||||||
|
|
||||||
/* Give a cycle to data setup. */
|
|
||||||
data_setup_in_cycles++;
|
|
||||||
/* Synchronize the data setup time with the cycles. */
|
|
||||||
data_setup_in_ns += clock_period_in_ns;
|
|
||||||
/* Adjust tEYE accordingly. */
|
|
||||||
tEYE += clock_period_in_ns;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decrease the ideal sample delay by one half cycle, to keep it
|
|
||||||
* in the middle of the eye.
|
|
||||||
*/
|
|
||||||
ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1);
|
|
||||||
|
|
||||||
/* Jam a negative ideal sample delay to zero. */
|
|
||||||
if (ideal_sample_delay_in_ns < 0)
|
|
||||||
ideal_sample_delay_in_ns = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compute the sample delay factor that corresponds to the ideal sample
|
|
||||||
* delay. If the result is too large, then use the maximum allowed
|
|
||||||
* value.
|
|
||||||
*
|
|
||||||
* Notice that we use the ns_to_cycles function to compute the sample
|
|
||||||
* delay factor. We do this because the form of the computation is the
|
|
||||||
* same as that for calculating cycles.
|
|
||||||
*/
|
|
||||||
sample_delay_factor =
|
|
||||||
ns_to_cycles(ideal_sample_delay_in_ns << dll_delay_shift,
|
|
||||||
clock_period_in_ns, 0);
|
|
||||||
|
|
||||||
if (sample_delay_factor > nfc->max_sample_delay_factor)
|
|
||||||
sample_delay_factor = nfc->max_sample_delay_factor;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These macros conveniently encapsulate a computation we'll use to
|
|
||||||
* continuously evaluate whether or not the data sample delay is inside
|
|
||||||
* the eye.
|
|
||||||
*/
|
|
||||||
#define IDEAL_DELAY ((int) ideal_sample_delay_in_ns)
|
|
||||||
|
|
||||||
#define QUANTIZED_DELAY \
|
|
||||||
((int) ((sample_delay_factor * clock_period_in_ns) >> \
|
|
||||||
dll_delay_shift))
|
|
||||||
|
|
||||||
#define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY))
|
|
||||||
|
|
||||||
#define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* While the quantized sample time falls outside the eye, reduce the
|
|
||||||
* sample delay or extend the data setup to move the sampling point back
|
|
||||||
* toward the eye. Do not allow the number of data setup cycles to
|
|
||||||
* exceed the maximum allowed by the NFC.
|
|
||||||
*/
|
|
||||||
while (SAMPLE_IS_NOT_WITHIN_THE_EYE &&
|
|
||||||
(data_setup_in_cycles < nfc->max_data_setup_cycles)) {
|
|
||||||
/*
|
|
||||||
* If control arrives here, the quantized sample delay falls
|
|
||||||
* outside the eye. Check if it's before the eye opens, or after
|
|
||||||
* the eye closes.
|
|
||||||
*/
|
|
||||||
if (QUANTIZED_DELAY > IDEAL_DELAY) {
|
|
||||||
/*
|
|
||||||
* If control arrives here, the quantized sample delay
|
|
||||||
* falls after the eye closes. Decrease the quantized
|
|
||||||
* delay time and then go back to re-evaluate.
|
|
||||||
*/
|
|
||||||
if (sample_delay_factor != 0)
|
|
||||||
sample_delay_factor--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If control arrives here, the quantized sample delay falls
|
|
||||||
* before the eye opens. Shift the sample point by increasing
|
|
||||||
* data setup time. This will also make the eye larger.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Give a cycle to data setup. */
|
|
||||||
data_setup_in_cycles++;
|
|
||||||
/* Synchronize the data setup time with the cycles. */
|
|
||||||
data_setup_in_ns += clock_period_in_ns;
|
|
||||||
/* Adjust tEYE accordingly. */
|
|
||||||
tEYE += clock_period_in_ns;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decrease the ideal sample delay by one half cycle, to keep it
|
|
||||||
* in the middle of the eye.
|
|
||||||
*/
|
|
||||||
ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1);
|
|
||||||
|
|
||||||
/* ...and one less period for the delay time. */
|
|
||||||
ideal_sample_delay_in_ns -= clock_period_in_ns;
|
|
||||||
|
|
||||||
/* Jam a negative ideal sample delay to zero. */
|
|
||||||
if (ideal_sample_delay_in_ns < 0)
|
|
||||||
ideal_sample_delay_in_ns = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We have a new ideal sample delay, so re-compute the quantized
|
|
||||||
* delay.
|
|
||||||
*/
|
|
||||||
sample_delay_factor =
|
|
||||||
ns_to_cycles(
|
|
||||||
ideal_sample_delay_in_ns << dll_delay_shift,
|
|
||||||
clock_period_in_ns, 0);
|
|
||||||
|
|
||||||
if (sample_delay_factor > nfc->max_sample_delay_factor)
|
|
||||||
sample_delay_factor = nfc->max_sample_delay_factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Control arrives here when we're ready to return our results. */
|
|
||||||
return_results:
|
|
||||||
hw->data_setup_in_cycles = data_setup_in_cycles;
|
|
||||||
hw->data_hold_in_cycles = data_hold_in_cycles;
|
|
||||||
hw->address_setup_in_cycles = address_setup_in_cycles;
|
|
||||||
hw->use_half_periods = dll_use_half_periods;
|
|
||||||
hw->sample_delay_factor = sample_delay_factor;
|
|
||||||
hw->device_busy_timeout = GPMI_DEFAULT_BUSY_TIMEOUT;
|
|
||||||
hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
|
|
||||||
|
|
||||||
/* Return success. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* <1> Firstly, we should know what's the GPMI-clock means.
|
* <1> Firstly, we should know what's the GPMI-clock means.
|
||||||
* The GPMI-clock is the internal clock in the gpmi nand controller.
|
* The GPMI-clock is the internal clock in the gpmi nand controller.
|
||||||
|
@ -824,13 +362,10 @@ return_results:
|
||||||
* 4.1) From the aspect of the nand chip pins:
|
* 4.1) From the aspect of the nand chip pins:
|
||||||
* Delay = (tREA + C - tRP) {1}
|
* Delay = (tREA + C - tRP) {1}
|
||||||
*
|
*
|
||||||
* tREA : the maximum read access time. From the ONFI nand standards,
|
* tREA : the maximum read access time.
|
||||||
* we know that tREA is 16ns in mode 5, tREA is 20ns is mode 4.
|
* C : a constant to adjust the delay. default is 4000ps.
|
||||||
* Please check it in : www.onfi.org
|
* tRP : the read pulse width, which is exactly:
|
||||||
* C : a constant for adjust the delay. default is 4.
|
* tRP = (GPMI-clock-period) * DATA_SETUP
|
||||||
* tRP : the read pulse width.
|
|
||||||
* Specified by the HW_GPMI_TIMING0:DATA_SETUP:
|
|
||||||
* tRP = (GPMI-clock-period) * DATA_SETUP
|
|
||||||
*
|
*
|
||||||
* 4.2) From the aspect of the GPMI nand controller:
|
* 4.2) From the aspect of the GPMI nand controller:
|
||||||
* Delay = RDN_DELAY * 0.125 * RP {2}
|
* Delay = RDN_DELAY * 0.125 * RP {2}
|
||||||
|
@ -843,239 +378,137 @@ return_results:
|
||||||
*
|
*
|
||||||
* Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period
|
* Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period
|
||||||
* is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD
|
* is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD
|
||||||
* is 16ns, but in mx6q, we use 12ns.
|
* is 16000ps, but in mx6q, we use 12000ps.
|
||||||
*
|
*
|
||||||
* 4.3) since {1} equals {2}, we get:
|
* 4.3) since {1} equals {2}, we get:
|
||||||
*
|
*
|
||||||
* (tREA + 4 - tRP) * 8
|
* (tREA + 4000 - tRP) * 8
|
||||||
* RDN_DELAY = --------------------- {3}
|
* RDN_DELAY = ----------------------- {3}
|
||||||
* RP
|
* RP
|
||||||
*
|
|
||||||
* 4.4) We only support the fastest asynchronous mode of ONFI nand.
|
|
||||||
* For some ONFI nand, the mode 4 is the fastest mode;
|
|
||||||
* while for some ONFI nand, the mode 5 is the fastest mode.
|
|
||||||
* So we only support the mode 4 and mode 5. It is no need to
|
|
||||||
* support other modes.
|
|
||||||
*/
|
*/
|
||||||
static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
|
static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
|
||||||
struct gpmi_nfc_hardware_timing *hw)
|
const struct nand_sdr_timings *sdr)
|
||||||
{
|
{
|
||||||
struct resources *r = &this->resources;
|
struct gpmi_nfc_hardware_timing *hw = &this->hw;
|
||||||
unsigned long rate = clk_get_rate(r->clock[0]);
|
unsigned int dll_threshold_ps = this->devdata->max_chain_delay;
|
||||||
int mode = this->timing_mode;
|
unsigned int period_ps, reference_period_ps;
|
||||||
int dll_threshold = this->devdata->max_chain_delay;
|
unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles;
|
||||||
unsigned long delay;
|
unsigned int tRP_ps;
|
||||||
unsigned long clk_period;
|
bool use_half_period;
|
||||||
int t_rea;
|
int sample_delay_ps, sample_delay_factor;
|
||||||
int c = 4;
|
u16 busy_timeout_cycles;
|
||||||
int t_rp;
|
u8 wrn_dly_sel;
|
||||||
int rp;
|
|
||||||
|
|
||||||
/*
|
if (sdr->tRC_min >= 30000) {
|
||||||
* [1] for GPMI_HW_GPMI_TIMING0:
|
/* ONFI non-EDO modes [0-3] */
|
||||||
* The async mode requires 40MHz for mode 4, 50MHz for mode 5.
|
hw->clk_rate = 22000000;
|
||||||
* The GPMI can support 100MHz at most. So if we want to
|
wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
|
||||||
* get the 40MHz or 50MHz, we have to set DS=1, DH=1.
|
} else if (sdr->tRC_min >= 25000) {
|
||||||
* Set the ADDRESS_SETUP to 0 in mode 4.
|
/* ONFI EDO mode 4 */
|
||||||
*/
|
hw->clk_rate = 80000000;
|
||||||
hw->data_setup_in_cycles = 1;
|
wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
|
||||||
hw->data_hold_in_cycles = 1;
|
|
||||||
hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0);
|
|
||||||
|
|
||||||
/* [2] for GPMI_HW_GPMI_TIMING1 */
|
|
||||||
hw->device_busy_timeout = 0x9000;
|
|
||||||
|
|
||||||
/* [3] for GPMI_HW_GPMI_CTRL1 */
|
|
||||||
hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enlarge 10 times for the numerator and denominator in {3}.
|
|
||||||
* This make us to get more accurate result.
|
|
||||||
*/
|
|
||||||
clk_period = NSEC_PER_SEC / (rate / 10);
|
|
||||||
dll_threshold *= 10;
|
|
||||||
t_rea = ((mode == 5) ? 16 : 20) * 10;
|
|
||||||
c *= 10;
|
|
||||||
|
|
||||||
t_rp = clk_period * 1; /* DATA_SETUP is 1 */
|
|
||||||
|
|
||||||
if (clk_period > dll_threshold) {
|
|
||||||
hw->use_half_periods = 1;
|
|
||||||
rp = clk_period / 2;
|
|
||||||
} else {
|
} else {
|
||||||
hw->use_half_periods = 0;
|
/* ONFI EDO mode 5 */
|
||||||
rp = clk_period;
|
hw->clk_rate = 100000000;
|
||||||
|
wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SDR core timings are given in picoseconds */
|
||||||
|
period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate);
|
||||||
|
|
||||||
|
addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps);
|
||||||
|
data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps);
|
||||||
|
data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps);
|
||||||
|
busy_timeout_cycles = TO_CYCLES(sdr->tWB_max + sdr->tR_max, period_ps);
|
||||||
|
|
||||||
|
hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) |
|
||||||
|
BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) |
|
||||||
|
BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles);
|
||||||
|
hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(busy_timeout_cycles * 4096);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Multiply the numerator with 10, we could do a round off:
|
* Derive NFC ideal delay from {3}:
|
||||||
* 7.8 round up to 8; 7.4 round down to 7.
|
*
|
||||||
|
* (tREA + 4000 - tRP) * 8
|
||||||
|
* RDN_DELAY = -----------------------
|
||||||
|
* RP
|
||||||
*/
|
*/
|
||||||
delay = (((t_rea + c - t_rp) * 8) * 10) / rp;
|
if (period_ps > dll_threshold_ps) {
|
||||||
delay = (delay + 5) / 10;
|
use_half_period = true;
|
||||||
|
reference_period_ps = period_ps / 2;
|
||||||
hw->sample_delay_factor = delay;
|
} else {
|
||||||
}
|
use_half_period = false;
|
||||||
|
reference_period_ps = period_ps;
|
||||||
static int enable_edo_mode(struct gpmi_nand_data *this, int mode)
|
|
||||||
{
|
|
||||||
struct resources *r = &this->resources;
|
|
||||||
struct nand_chip *nand = &this->nand;
|
|
||||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
|
||||||
uint8_t *feature;
|
|
||||||
unsigned long rate;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
feature = kzalloc(ONFI_SUBFEATURE_PARAM_LEN, GFP_KERNEL);
|
|
||||||
if (!feature)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
nand->select_chip(mtd, 0);
|
|
||||||
|
|
||||||
/* [1] send SET FEATURE command to NAND */
|
|
||||||
feature[0] = mode;
|
|
||||||
ret = nand->onfi_set_features(mtd, nand,
|
|
||||||
ONFI_FEATURE_ADDR_TIMING_MODE, feature);
|
|
||||||
if (ret)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
/* [2] send GET FEATURE command to double-check the timing mode */
|
|
||||||
memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN);
|
|
||||||
ret = nand->onfi_get_features(mtd, nand,
|
|
||||||
ONFI_FEATURE_ADDR_TIMING_MODE, feature);
|
|
||||||
if (ret || feature[0] != mode)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
nand->select_chip(mtd, -1);
|
|
||||||
|
|
||||||
/* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */
|
|
||||||
rate = (mode == 5) ? 100000000 : 80000000;
|
|
||||||
clk_set_rate(r->clock[0], rate);
|
|
||||||
|
|
||||||
/* Let the gpmi_begin() re-compute the timing again. */
|
|
||||||
this->flags &= ~GPMI_TIMING_INIT_OK;
|
|
||||||
|
|
||||||
this->flags |= GPMI_ASYNC_EDO_ENABLED;
|
|
||||||
this->timing_mode = mode;
|
|
||||||
kfree(feature);
|
|
||||||
dev_info(this->dev, "enable the asynchronous EDO mode %d\n", mode);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
nand->select_chip(mtd, -1);
|
|
||||||
kfree(feature);
|
|
||||||
dev_err(this->dev, "mode:%d ,failed in set feature.\n", mode);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gpmi_extra_init(struct gpmi_nand_data *this)
|
|
||||||
{
|
|
||||||
struct nand_chip *chip = &this->nand;
|
|
||||||
|
|
||||||
/* Enable the asynchronous EDO feature. */
|
|
||||||
if (GPMI_IS_MX6(this) && chip->onfi_version) {
|
|
||||||
int mode = onfi_get_async_timing_mode(chip);
|
|
||||||
|
|
||||||
/* We only support the timing mode 4 and mode 5. */
|
|
||||||
if (mode & ONFI_TIMING_MODE_5)
|
|
||||||
mode = 5;
|
|
||||||
else if (mode & ONFI_TIMING_MODE_4)
|
|
||||||
mode = 4;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return enable_edo_mode(this, mode);
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
tRP_ps = data_setup_cycles * period_ps;
|
||||||
|
sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8;
|
||||||
|
if (sample_delay_ps > 0)
|
||||||
|
sample_delay_factor = sample_delay_ps / reference_period_ps;
|
||||||
|
else
|
||||||
|
sample_delay_factor = 0;
|
||||||
|
|
||||||
|
hw->ctrl1n = BF_GPMI_CTRL1_WRN_DLY_SEL(wrn_dly_sel);
|
||||||
|
if (sample_delay_factor)
|
||||||
|
hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) |
|
||||||
|
BM_GPMI_CTRL1_DLL_ENABLE |
|
||||||
|
(use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Begin the I/O */
|
void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
|
||||||
void gpmi_begin(struct gpmi_nand_data *this)
|
|
||||||
{
|
{
|
||||||
|
struct gpmi_nfc_hardware_timing *hw = &this->hw;
|
||||||
struct resources *r = &this->resources;
|
struct resources *r = &this->resources;
|
||||||
void __iomem *gpmi_regs = r->gpmi_regs;
|
void __iomem *gpmi_regs = r->gpmi_regs;
|
||||||
unsigned int clock_period_in_ns;
|
unsigned int dll_wait_time_us;
|
||||||
uint32_t reg;
|
|
||||||
unsigned int dll_wait_time_in_us;
|
|
||||||
struct gpmi_nfc_hardware_timing hw;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Enable the clock. */
|
clk_set_rate(r->clock[0], hw->clk_rate);
|
||||||
ret = gpmi_enable_clk(this);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(this->dev, "We failed in enable the clk\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only initialize the timing once */
|
writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0);
|
||||||
if (this->flags & GPMI_TIMING_INIT_OK)
|
writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1);
|
||||||
return;
|
|
||||||
this->flags |= GPMI_TIMING_INIT_OK;
|
|
||||||
|
|
||||||
if (this->flags & GPMI_ASYNC_EDO_ENABLED)
|
|
||||||
gpmi_compute_edo_timing(this, &hw);
|
|
||||||
else
|
|
||||||
gpmi_nfc_compute_hardware_timing(this, &hw);
|
|
||||||
|
|
||||||
/* [1] Set HW_GPMI_TIMING0 */
|
|
||||||
reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) |
|
|
||||||
BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) |
|
|
||||||
BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles);
|
|
||||||
|
|
||||||
writel(reg, gpmi_regs + HW_GPMI_TIMING0);
|
|
||||||
|
|
||||||
/* [2] Set HW_GPMI_TIMING1 */
|
|
||||||
writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw.device_busy_timeout),
|
|
||||||
gpmi_regs + HW_GPMI_TIMING1);
|
|
||||||
|
|
||||||
/* [3] The following code is to set the HW_GPMI_CTRL1. */
|
|
||||||
|
|
||||||
/* Set the WRN_DLY_SEL */
|
|
||||||
writel(BM_GPMI_CTRL1_WRN_DLY_SEL, gpmi_regs + HW_GPMI_CTRL1_CLR);
|
|
||||||
writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw.wrn_dly_sel),
|
|
||||||
gpmi_regs + HW_GPMI_CTRL1_SET);
|
|
||||||
|
|
||||||
/* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. */
|
|
||||||
writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR);
|
|
||||||
|
|
||||||
/* Clear out the DLL control fields. */
|
|
||||||
reg = BM_GPMI_CTRL1_RDN_DELAY | BM_GPMI_CTRL1_HALF_PERIOD;
|
|
||||||
writel(reg, gpmi_regs + HW_GPMI_CTRL1_CLR);
|
|
||||||
|
|
||||||
/* If no sample delay is called for, return immediately. */
|
|
||||||
if (!hw.sample_delay_factor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Set RDN_DELAY or HALF_PERIOD. */
|
|
||||||
reg = ((hw.use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0)
|
|
||||||
| BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor);
|
|
||||||
|
|
||||||
writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET);
|
|
||||||
|
|
||||||
/* At last, we enable the DLL. */
|
|
||||||
writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* After we enable the GPMI DLL, we have to wait 64 clock cycles before
|
* Clear several CTRL1 fields, DLL must be disabled when setting
|
||||||
* we can use the GPMI. Calculate the amount of time we need to wait,
|
* RDN_DELAY or HALF_PERIOD.
|
||||||
* in microseconds.
|
|
||||||
*/
|
*/
|
||||||
clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]);
|
writel(BM_GPMI_CTRL1_CLEAR_MASK, gpmi_regs + HW_GPMI_CTRL1_CLR);
|
||||||
dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000;
|
writel(hw->ctrl1n, gpmi_regs + HW_GPMI_CTRL1_SET);
|
||||||
|
|
||||||
if (!dll_wait_time_in_us)
|
/* Wait 64 clock cycles before using the GPMI after enabling the DLL */
|
||||||
dll_wait_time_in_us = 1;
|
dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64;
|
||||||
|
if (!dll_wait_time_us)
|
||||||
|
dll_wait_time_us = 1;
|
||||||
|
|
||||||
/* Wait for the DLL to settle. */
|
/* Wait for the DLL to settle. */
|
||||||
udelay(dll_wait_time_in_us);
|
udelay(dll_wait_time_us);
|
||||||
|
|
||||||
err_out:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpmi_end(struct gpmi_nand_data *this)
|
int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
||||||
|
const struct nand_data_interface *conf)
|
||||||
{
|
{
|
||||||
gpmi_disable_clk(this);
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct gpmi_nand_data *this = nand_get_controller_data(chip);
|
||||||
|
const struct nand_sdr_timings *sdr;
|
||||||
|
|
||||||
|
/* Retrieve required NAND timings */
|
||||||
|
sdr = nand_get_sdr_timings(conf);
|
||||||
|
if (IS_ERR(sdr))
|
||||||
|
return PTR_ERR(sdr);
|
||||||
|
|
||||||
|
/* Only MX6 GPMI controller can reach EDO timings */
|
||||||
|
if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this))
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
/* Stop here if this call was just a check */
|
||||||
|
if (chipnr < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Do the actual derivation of the controller timings */
|
||||||
|
gpmi_nfc_compute_timings(this, sdr);
|
||||||
|
|
||||||
|
this->hw.must_apply_timings = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clears a BCH interrupt. */
|
/* Clears a BCH interrupt. */
|
|
@ -94,7 +94,7 @@ static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = {
|
||||||
static const struct gpmi_devdata gpmi_devdata_imx23 = {
|
static const struct gpmi_devdata gpmi_devdata_imx23 = {
|
||||||
.type = IS_MX23,
|
.type = IS_MX23,
|
||||||
.bch_max_ecc_strength = 20,
|
.bch_max_ecc_strength = 20,
|
||||||
.max_chain_delay = 16,
|
.max_chain_delay = 16000,
|
||||||
.clks = gpmi_clks_for_mx2x,
|
.clks = gpmi_clks_for_mx2x,
|
||||||
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
|
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
|
||||||
};
|
};
|
||||||
|
@ -102,7 +102,7 @@ static const struct gpmi_devdata gpmi_devdata_imx23 = {
|
||||||
static const struct gpmi_devdata gpmi_devdata_imx28 = {
|
static const struct gpmi_devdata gpmi_devdata_imx28 = {
|
||||||
.type = IS_MX28,
|
.type = IS_MX28,
|
||||||
.bch_max_ecc_strength = 20,
|
.bch_max_ecc_strength = 20,
|
||||||
.max_chain_delay = 16,
|
.max_chain_delay = 16000,
|
||||||
.clks = gpmi_clks_for_mx2x,
|
.clks = gpmi_clks_for_mx2x,
|
||||||
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
|
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
|
||||||
};
|
};
|
||||||
|
@ -114,7 +114,7 @@ static const char * const gpmi_clks_for_mx6[] = {
|
||||||
static const struct gpmi_devdata gpmi_devdata_imx6q = {
|
static const struct gpmi_devdata gpmi_devdata_imx6q = {
|
||||||
.type = IS_MX6Q,
|
.type = IS_MX6Q,
|
||||||
.bch_max_ecc_strength = 40,
|
.bch_max_ecc_strength = 40,
|
||||||
.max_chain_delay = 12,
|
.max_chain_delay = 12000,
|
||||||
.clks = gpmi_clks_for_mx6,
|
.clks = gpmi_clks_for_mx6,
|
||||||
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
|
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
|
||||||
};
|
};
|
||||||
|
@ -122,7 +122,7 @@ static const struct gpmi_devdata gpmi_devdata_imx6q = {
|
||||||
static const struct gpmi_devdata gpmi_devdata_imx6sx = {
|
static const struct gpmi_devdata gpmi_devdata_imx6sx = {
|
||||||
.type = IS_MX6SX,
|
.type = IS_MX6SX,
|
||||||
.bch_max_ecc_strength = 62,
|
.bch_max_ecc_strength = 62,
|
||||||
.max_chain_delay = 12,
|
.max_chain_delay = 12000,
|
||||||
.clks = gpmi_clks_for_mx6,
|
.clks = gpmi_clks_for_mx6,
|
||||||
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
|
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
|
||||||
};
|
};
|
||||||
|
@ -134,7 +134,7 @@ static const char * const gpmi_clks_for_mx7d[] = {
|
||||||
static const struct gpmi_devdata gpmi_devdata_imx7d = {
|
static const struct gpmi_devdata gpmi_devdata_imx7d = {
|
||||||
.type = IS_MX7D,
|
.type = IS_MX7D,
|
||||||
.bch_max_ecc_strength = 62,
|
.bch_max_ecc_strength = 62,
|
||||||
.max_chain_delay = 12,
|
.max_chain_delay = 12000,
|
||||||
.clks = gpmi_clks_for_mx7d,
|
.clks = gpmi_clks_for_mx7d,
|
||||||
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d),
|
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d),
|
||||||
};
|
};
|
||||||
|
@ -695,34 +695,6 @@ static void release_resources(struct gpmi_nand_data *this)
|
||||||
release_dma_channels(this);
|
release_dma_channels(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int init_hardware(struct gpmi_nand_data *this)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This structure contains the "safe" GPMI timing that should succeed
|
|
||||||
* with any NAND Flash device
|
|
||||||
* (although, with less-than-optimal performance).
|
|
||||||
*/
|
|
||||||
struct nand_timing safe_timing = {
|
|
||||||
.data_setup_in_ns = 80,
|
|
||||||
.data_hold_in_ns = 60,
|
|
||||||
.address_setup_in_ns = 25,
|
|
||||||
.gpmi_sample_delay_in_ns = 6,
|
|
||||||
.tREA_in_ns = -1,
|
|
||||||
.tRLOH_in_ns = -1,
|
|
||||||
.tRHOH_in_ns = -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Initialize the hardwares. */
|
|
||||||
ret = gpmi_init(this);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
this->timing = safe_timing;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_page_prepare(struct gpmi_nand_data *this,
|
static int read_page_prepare(struct gpmi_nand_data *this,
|
||||||
void *destination, unsigned length,
|
void *destination, unsigned length,
|
||||||
void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
|
void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
|
||||||
|
@ -938,11 +910,32 @@ static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
struct gpmi_nand_data *this = nand_get_controller_data(chip);
|
struct gpmi_nand_data *this = nand_get_controller_data(chip);
|
||||||
|
int ret;
|
||||||
|
|
||||||
if ((this->current_chip < 0) && (chipnr >= 0))
|
/*
|
||||||
gpmi_begin(this);
|
* For power consumption matters, disable/enable the clock each time a
|
||||||
else if ((this->current_chip >= 0) && (chipnr < 0))
|
* die is selected/unselected.
|
||||||
gpmi_end(this);
|
*/
|
||||||
|
if (this->current_chip < 0 && chipnr >= 0) {
|
||||||
|
ret = gpmi_enable_clk(this);
|
||||||
|
if (ret)
|
||||||
|
dev_err(this->dev, "Failed to enable the clock\n");
|
||||||
|
} else if (this->current_chip >= 0 && chipnr < 0) {
|
||||||
|
ret = gpmi_disable_clk(this);
|
||||||
|
if (ret)
|
||||||
|
dev_err(this->dev, "Failed to disable the clock\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This driver currently supports only one NAND chip. Plus, dies share
|
||||||
|
* the same configuration. So once timings have been applied on the
|
||||||
|
* controller side, they will not change anymore. When the time will
|
||||||
|
* come, the check on must_apply_timings will have to be dropped.
|
||||||
|
*/
|
||||||
|
if (chipnr >= 0 && this->hw.must_apply_timings) {
|
||||||
|
this->hw.must_apply_timings = false;
|
||||||
|
gpmi_nfc_apply_timings(this);
|
||||||
|
}
|
||||||
|
|
||||||
this->current_chip = chipnr;
|
this->current_chip = chipnr;
|
||||||
}
|
}
|
||||||
|
@ -1955,14 +1948,6 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
|
||||||
chip->options |= NAND_SUBPAGE_READ;
|
chip->options |= NAND_SUBPAGE_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Can we enable the extra features? such as EDO or Sync mode.
|
|
||||||
*
|
|
||||||
* We do not check the return value now. That's means if we fail in
|
|
||||||
* enable the extra features, we still can run in the normal way.
|
|
||||||
*/
|
|
||||||
gpmi_extra_init(this);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1983,6 +1968,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
|
||||||
nand_set_controller_data(chip, this);
|
nand_set_controller_data(chip, this);
|
||||||
nand_set_flash_node(chip, this->pdev->dev.of_node);
|
nand_set_flash_node(chip, this->pdev->dev.of_node);
|
||||||
chip->select_chip = gpmi_select_chip;
|
chip->select_chip = gpmi_select_chip;
|
||||||
|
chip->setup_data_interface = gpmi_setup_data_interface;
|
||||||
chip->cmd_ctrl = gpmi_cmd_ctrl;
|
chip->cmd_ctrl = gpmi_cmd_ctrl;
|
||||||
chip->dev_ready = gpmi_dev_ready;
|
chip->dev_ready = gpmi_dev_ready;
|
||||||
chip->read_byte = gpmi_read_byte;
|
chip->read_byte = gpmi_read_byte;
|
||||||
|
@ -2093,7 +2079,7 @@ static int gpmi_nand_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto exit_acquire_resources;
|
goto exit_acquire_resources;
|
||||||
|
|
||||||
ret = init_hardware(this);
|
ret = gpmi_init(this);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto exit_nfc_init;
|
goto exit_nfc_init;
|
||||||
|
|
||||||
|
@ -2141,7 +2127,6 @@ static int gpmi_pm_resume(struct device *dev)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* re-init the GPMI registers */
|
/* re-init the GPMI registers */
|
||||||
this->flags &= ~GPMI_TIMING_INIT_OK;
|
|
||||||
ret = gpmi_init(this);
|
ret = gpmi_init(this);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(this->dev, "Error setting GPMI : %d\n", ret);
|
dev_err(this->dev, "Error setting GPMI : %d\n", ret);
|
||||||
|
@ -2155,9 +2140,6 @@ static int gpmi_pm_resume(struct device *dev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* re-init others */
|
|
||||||
gpmi_extra_init(this);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PM_SLEEP */
|
#endif /* CONFIG_PM_SLEEP */
|
|
@ -86,39 +86,6 @@ enum dma_ops_type {
|
||||||
DMA_FOR_WRITE_ECC_PAGE
|
DMA_FOR_WRITE_ECC_PAGE
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* struct nand_timing - Fundamental timing attributes for NAND.
|
|
||||||
* @data_setup_in_ns: The data setup time, in nanoseconds. Usually the
|
|
||||||
* maximum of tDS and tWP. A negative value
|
|
||||||
* indicates this characteristic isn't known.
|
|
||||||
* @data_hold_in_ns: The data hold time, in nanoseconds. Usually the
|
|
||||||
* maximum of tDH, tWH and tREH. A negative value
|
|
||||||
* indicates this characteristic isn't known.
|
|
||||||
* @address_setup_in_ns: The address setup time, in nanoseconds. Usually
|
|
||||||
* the maximum of tCLS, tCS and tALS. A negative
|
|
||||||
* value indicates this characteristic isn't known.
|
|
||||||
* @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value
|
|
||||||
* indicates this characteristic isn't known.
|
|
||||||
* @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A
|
|
||||||
* negative value indicates this characteristic isn't
|
|
||||||
* known.
|
|
||||||
* @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A
|
|
||||||
* negative value indicates this characteristic isn't
|
|
||||||
* known.
|
|
||||||
* @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A
|
|
||||||
* negative value indicates this characteristic isn't
|
|
||||||
* known.
|
|
||||||
*/
|
|
||||||
struct nand_timing {
|
|
||||||
int8_t data_setup_in_ns;
|
|
||||||
int8_t data_hold_in_ns;
|
|
||||||
int8_t address_setup_in_ns;
|
|
||||||
int8_t gpmi_sample_delay_in_ns;
|
|
||||||
int8_t tREA_in_ns;
|
|
||||||
int8_t tRLOH_in_ns;
|
|
||||||
int8_t tRHOH_in_ns;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum gpmi_type {
|
enum gpmi_type {
|
||||||
IS_MX23,
|
IS_MX23,
|
||||||
IS_MX28,
|
IS_MX28,
|
||||||
|
@ -135,11 +102,27 @@ struct gpmi_devdata {
|
||||||
const int clks_count;
|
const int clks_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
|
||||||
|
* @must_apply_timings: Whether controller timings have already been
|
||||||
|
* applied or not (useful only while there is
|
||||||
|
* support for only one chip select)
|
||||||
|
* @clk_rate: The clock rate that must be used to derive the
|
||||||
|
* following parameters
|
||||||
|
* @timing0: HW_GPMI_TIMING0 register
|
||||||
|
* @timing1: HW_GPMI_TIMING1 register
|
||||||
|
* @ctrl1n: HW_GPMI_CTRL1n register
|
||||||
|
*/
|
||||||
|
struct gpmi_nfc_hardware_timing {
|
||||||
|
bool must_apply_timings;
|
||||||
|
unsigned long int clk_rate;
|
||||||
|
u32 timing0;
|
||||||
|
u32 timing1;
|
||||||
|
u32 ctrl1n;
|
||||||
|
};
|
||||||
|
|
||||||
struct gpmi_nand_data {
|
struct gpmi_nand_data {
|
||||||
/* flags */
|
/* Devdata */
|
||||||
#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
|
|
||||||
#define GPMI_TIMING_INIT_OK (1 << 1)
|
|
||||||
int flags;
|
|
||||||
const struct gpmi_devdata *devdata;
|
const struct gpmi_devdata *devdata;
|
||||||
|
|
||||||
/* System Interface */
|
/* System Interface */
|
||||||
|
@ -150,8 +133,7 @@ struct gpmi_nand_data {
|
||||||
struct resources resources;
|
struct resources resources;
|
||||||
|
|
||||||
/* Flash Hardware */
|
/* Flash Hardware */
|
||||||
struct nand_timing timing;
|
struct gpmi_nfc_hardware_timing hw;
|
||||||
int timing_mode;
|
|
||||||
|
|
||||||
/* BCH */
|
/* BCH */
|
||||||
struct bch_geometry bch_geometry;
|
struct bch_geometry bch_geometry;
|
||||||
|
@ -204,69 +186,6 @@ struct gpmi_nand_data {
|
||||||
void *private;
|
void *private;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
|
|
||||||
* @data_setup_in_cycles: The data setup time, in cycles.
|
|
||||||
* @data_hold_in_cycles: The data hold time, in cycles.
|
|
||||||
* @address_setup_in_cycles: The address setup time, in cycles.
|
|
||||||
* @device_busy_timeout: The timeout waiting for NAND Ready/Busy,
|
|
||||||
* this value is the number of cycles multiplied
|
|
||||||
* by 4096.
|
|
||||||
* @use_half_periods: Indicates the clock is running slowly, so the
|
|
||||||
* NFC DLL should use half-periods.
|
|
||||||
* @sample_delay_factor: The sample delay factor.
|
|
||||||
* @wrn_dly_sel: The delay on the GPMI write strobe.
|
|
||||||
*/
|
|
||||||
struct gpmi_nfc_hardware_timing {
|
|
||||||
/* for HW_GPMI_TIMING0 */
|
|
||||||
uint8_t data_setup_in_cycles;
|
|
||||||
uint8_t data_hold_in_cycles;
|
|
||||||
uint8_t address_setup_in_cycles;
|
|
||||||
|
|
||||||
/* for HW_GPMI_TIMING1 */
|
|
||||||
uint16_t device_busy_timeout;
|
|
||||||
#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/
|
|
||||||
|
|
||||||
/* for HW_GPMI_CTRL1 */
|
|
||||||
bool use_half_periods;
|
|
||||||
uint8_t sample_delay_factor;
|
|
||||||
uint8_t wrn_dly_sel;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct timing_threshold - Timing threshold
|
|
||||||
* @max_data_setup_cycles: The maximum number of data setup cycles that
|
|
||||||
* can be expressed in the hardware.
|
|
||||||
* @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires
|
|
||||||
* for data read internal setup. In the Reference
|
|
||||||
* Manual, see the chapter "High-Speed NAND
|
|
||||||
* Timing" for more details.
|
|
||||||
* @max_sample_delay_factor: The maximum sample delay factor that can be
|
|
||||||
* expressed in the hardware.
|
|
||||||
* @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the
|
|
||||||
* sample delay DLL hardware can possibly work
|
|
||||||
* with (the DLL is unusable with longer periods).
|
|
||||||
* If the full-cycle period is greater than HALF
|
|
||||||
* this value, the DLL must be configured to use
|
|
||||||
* half-periods.
|
|
||||||
* @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the
|
|
||||||
* DLL can implement.
|
|
||||||
* @clock_frequency_in_hz: The clock frequency, in Hz, during the current
|
|
||||||
* I/O transaction. If no I/O transaction is in
|
|
||||||
* progress, this is the clock frequency during
|
|
||||||
* the most recent I/O transaction.
|
|
||||||
*/
|
|
||||||
struct timing_threshold {
|
|
||||||
const unsigned int max_chip_count;
|
|
||||||
const unsigned int max_data_setup_cycles;
|
|
||||||
const unsigned int internal_data_setup_in_ns;
|
|
||||||
const unsigned int max_sample_delay_factor;
|
|
||||||
const unsigned int max_dll_clock_period_in_ns;
|
|
||||||
const unsigned int max_dll_delay_in_ns;
|
|
||||||
unsigned long clock_frequency_in_hz;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Common Services */
|
/* Common Services */
|
||||||
int common_nfc_set_geometry(struct gpmi_nand_data *);
|
int common_nfc_set_geometry(struct gpmi_nand_data *);
|
||||||
struct dma_chan *get_dma_chan(struct gpmi_nand_data *);
|
struct dma_chan *get_dma_chan(struct gpmi_nand_data *);
|
||||||
|
@ -279,14 +198,16 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *,
|
||||||
|
|
||||||
/* GPMI-NAND helper function library */
|
/* GPMI-NAND helper function library */
|
||||||
int gpmi_init(struct gpmi_nand_data *);
|
int gpmi_init(struct gpmi_nand_data *);
|
||||||
int gpmi_extra_init(struct gpmi_nand_data *);
|
|
||||||
void gpmi_clear_bch(struct gpmi_nand_data *);
|
void gpmi_clear_bch(struct gpmi_nand_data *);
|
||||||
void gpmi_dump_info(struct gpmi_nand_data *);
|
void gpmi_dump_info(struct gpmi_nand_data *);
|
||||||
int bch_set_geometry(struct gpmi_nand_data *);
|
int bch_set_geometry(struct gpmi_nand_data *);
|
||||||
int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
|
int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
|
||||||
int gpmi_send_command(struct gpmi_nand_data *);
|
int gpmi_send_command(struct gpmi_nand_data *);
|
||||||
void gpmi_begin(struct gpmi_nand_data *);
|
int gpmi_enable_clk(struct gpmi_nand_data *this);
|
||||||
void gpmi_end(struct gpmi_nand_data *);
|
int gpmi_disable_clk(struct gpmi_nand_data *this);
|
||||||
|
int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
||||||
|
const struct nand_data_interface *conf);
|
||||||
|
void gpmi_nfc_apply_timings(struct gpmi_nand_data *this);
|
||||||
int gpmi_read_data(struct gpmi_nand_data *);
|
int gpmi_read_data(struct gpmi_nand_data *);
|
||||||
int gpmi_send_data(struct gpmi_nand_data *);
|
int gpmi_send_data(struct gpmi_nand_data *);
|
||||||
int gpmi_send_page(struct gpmi_nand_data *,
|
int gpmi_send_page(struct gpmi_nand_data *,
|
|
@ -147,6 +147,11 @@
|
||||||
|
|
||||||
#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0)
|
#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0)
|
||||||
|
|
||||||
|
#define BM_GPMI_CTRL1_CLEAR_MASK (BM_GPMI_CTRL1_WRN_DLY_SEL | \
|
||||||
|
BM_GPMI_CTRL1_DLL_ENABLE | \
|
||||||
|
BM_GPMI_CTRL1_RDN_DELAY | \
|
||||||
|
BM_GPMI_CTRL1_HALF_PERIOD)
|
||||||
|
|
||||||
#define HW_GPMI_TIMING0 0x00000070
|
#define HW_GPMI_TIMING0 0x00000070
|
||||||
|
|
||||||
#define BP_GPMI_TIMING0_ADDRESS_SETUP 16
|
#define BP_GPMI_TIMING0_ADDRESS_SETUP 16
|
|
@ -762,8 +762,8 @@ static int hisi_nfc_probe(struct platform_device *pdev)
|
||||||
chip->write_buf = hisi_nfc_write_buf;
|
chip->write_buf = hisi_nfc_write_buf;
|
||||||
chip->read_buf = hisi_nfc_read_buf;
|
chip->read_buf = hisi_nfc_read_buf;
|
||||||
chip->chip_delay = HINFC504_CHIP_DELAY;
|
chip->chip_delay = HINFC504_CHIP_DELAY;
|
||||||
chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
|
chip->set_features = nand_get_set_features_notsupp;
|
||||||
chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
|
chip->get_features = nand_get_set_features_notsupp;
|
||||||
|
|
||||||
hisi_nfc_host_init(host);
|
hisi_nfc_host_init(host);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue