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:
Linus Torvalds 2018-04-06 12:15:41 -07:00
commit 3fd14cdcc0
170 changed files with 3711 additions and 4379 deletions

View File

@ -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>

View File

@ -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

View File

@ -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>;
};
};

View File

@ -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).

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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>

View File

@ -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"

View File

@ -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/

View File

@ -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)

View File

@ -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,

View File

@ -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;
} }

View File

@ -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,

View File

@ -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;
} }

View File

@ -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,

View File

@ -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;

View File

@ -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;
} }

View File

@ -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);
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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);
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -380,14 +380,8 @@ static int lpddr2_nvm_write(struct mtd_info *mtd, loff_t start_add,
*/ */
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;
} }
/* /*

View File

@ -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;
} }

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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;

View File

@ -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.
*/ */

View File

@ -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;

View File

@ -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)

View File

@ -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 @@ int del_mtd_device(struct mtd_info *mtd)
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.

View File

@ -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);

View File

@ -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

View File

@ -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 @@ static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb)
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;
} }

View File

@ -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

View File

@ -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
View 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);

View File

@ -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
View 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");

View File

@ -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>
* *

View File

@ -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

View File

@ -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;
} }

View File

@ -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

View 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

View 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

View File

@ -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;

View File

@ -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)
* *
* *

View File

@ -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

View File

@ -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)
* *
* *

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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:

View File

@ -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)
* *

View File

@ -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;
} }

View File

@ -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);

View File

@ -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:

View File

@ -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;
} }

View File

@ -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;

View File

@ -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:

View File

@ -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;

View File

@ -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 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
*/ */
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)

View File

@ -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>

View File

@ -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 @@ static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v)
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 @@ int bch_set_geometry(struct gpmi_nand_data *this)
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 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
* 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 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
* *
* 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. */

View File

@ -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 */

View File

@ -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 *,

View File

@ -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

View File

@ -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