summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.mailmap7
-rw-r--r--Documentation/arm/Samsung-S3C24XX/S3C2412.txt2
-rw-r--r--Documentation/devicetree/bindings/mtd/fsl-quadspi.txt24
-rw-r--r--Documentation/devicetree/bindings/mtd/marvell-nand.txt5
-rw-r--r--Documentation/devicetree/bindings/mtd/mtd-physmap.txt7
-rw-r--r--Documentation/devicetree/bindings/mtd/sunxi-nand.txt4
-rw-r--r--Documentation/driver-api/gpio/drivers-on-gpio.rst4
-rw-r--r--Documentation/driver-api/mtdnand.rst8
-rw-r--r--MAINTAINERS36
-rw-r--r--drivers/mtd/Kconfig2
-rw-r--r--drivers/mtd/Makefile2
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c16
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c26
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c3
-rw-r--r--drivers/mtd/chips/jedec_probe.c32
-rw-r--r--drivers/mtd/chips/map_ram.c2
-rw-r--r--drivers/mtd/devices/bcm47xxsflash.c12
-rw-r--r--drivers/mtd/devices/block2mtd.c9
-rw-r--r--drivers/mtd/devices/docg3.c16
-rw-r--r--drivers/mtd/devices/lart.c6
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c4
-rw-r--r--drivers/mtd/devices/mtdram.c3
-rw-r--r--drivers/mtd/devices/phram.c7
-rw-r--r--drivers/mtd/devices/pmc551.c2
-rw-r--r--drivers/mtd/devices/powernv_flash.c12
-rw-r--r--drivers/mtd/devices/slram.c7
-rw-r--r--drivers/mtd/devices/spear_smi.c3
-rw-r--r--drivers/mtd/devices/sst25l.c3
-rw-r--r--drivers/mtd/devices/st_spi_fsm.c7
-rw-r--r--drivers/mtd/ftl.c56
-rw-r--r--drivers/mtd/inftlmount.c8
-rw-r--r--drivers/mtd/lpddr/lpddr2_nvm.c10
-rw-r--r--drivers/mtd/lpddr/lpddr_cmds.c2
-rw-r--r--drivers/mtd/maps/Kconfig10
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/bfin-async-flash.c196
-rw-r--r--drivers/mtd/maps/physmap_of_core.c6
-rw-r--r--drivers/mtd/mtdblock.c21
-rw-r--r--drivers/mtd/mtdchar.c34
-rw-r--r--drivers/mtd/mtdconcat.c48
-rw-r--r--drivers/mtd/mtdcore.c94
-rw-r--r--drivers/mtd/mtdoops.c20
-rw-r--r--drivers/mtd/mtdpart.c139
-rw-r--r--drivers/mtd/mtdswap.c34
-rw-r--r--drivers/mtd/nand/Kconfig569
-rw-r--r--drivers/mtd/nand/Makefile71
-rw-r--r--drivers/mtd/nand/bbt.c130
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c862
-rw-r--r--drivers/mtd/nand/core.c244
-rw-r--r--drivers/mtd/nand/onenand/Kconfig (renamed from drivers/mtd/onenand/Kconfig)0
-rw-r--r--drivers/mtd/nand/onenand/Makefile (renamed from drivers/mtd/onenand/Makefile)0
-rw-r--r--drivers/mtd/nand/onenand/generic.c (renamed from drivers/mtd/onenand/generic.c)2
-rw-r--r--drivers/mtd/nand/onenand/omap2.c (renamed from drivers/mtd/onenand/omap2.c)2
-rw-r--r--drivers/mtd/nand/onenand/onenand_base.c (renamed from drivers/mtd/onenand/onenand_base.c)19
-rw-r--r--drivers/mtd/nand/onenand/onenand_bbt.c (renamed from drivers/mtd/onenand/onenand_bbt.c)2
-rw-r--r--drivers/mtd/nand/onenand/samsung.c (renamed from drivers/mtd/onenand/samsung.c)0
-rw-r--r--drivers/mtd/nand/onenand/samsung.h (renamed from drivers/mtd/onenand/samsung.h)0
-rw-r--r--drivers/mtd/nand/raw/Kconfig537
-rw-r--r--drivers/mtd/nand/raw/Makefile66
-rw-r--r--drivers/mtd/nand/raw/ams-delta.c (renamed from drivers/mtd/nand/ams-delta.c)13
-rw-r--r--drivers/mtd/nand/raw/atmel/Makefile (renamed from drivers/mtd/nand/atmel/Makefile)0
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c (renamed from drivers/mtd/nand/atmel/nand-controller.c)4
-rw-r--r--drivers/mtd/nand/raw/atmel/pmecc.c (renamed from drivers/mtd/nand/atmel/pmecc.c)4
-rw-r--r--drivers/mtd/nand/raw/atmel/pmecc.h (renamed from drivers/mtd/nand/atmel/pmecc.h)4
-rw-r--r--drivers/mtd/nand/raw/au1550nd.c (renamed from drivers/mtd/nand/au1550nd.c)2
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/Makefile (renamed from drivers/mtd/nand/bcm47xxnflash/Makefile)0
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h (renamed from drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h)0
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/main.c (renamed from drivers/mtd/nand/bcm47xxnflash/main.c)0
-rw-r--r--drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c (renamed from drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c)4
-rw-r--r--drivers/mtd/nand/raw/brcmnand/Makefile (renamed from drivers/mtd/nand/brcmnand/Makefile)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c (renamed from drivers/mtd/nand/brcmnand/bcm63138_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c (renamed from drivers/mtd/nand/brcmnand/bcm6368_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.c (renamed from drivers/mtd/nand/brcmnand/brcmnand.c)6
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.h (renamed from drivers/mtd/nand/brcmnand/brcmnand.h)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c (renamed from drivers/mtd/nand/brcmnand/brcmstb_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/brcmnand/iproc_nand.c (renamed from drivers/mtd/nand/brcmnand/iproc_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/cafe_nand.c (renamed from drivers/mtd/nand/cafe_nand.c)14
-rw-r--r--drivers/mtd/nand/raw/cmx270_nand.c (renamed from drivers/mtd/nand/cmx270_nand.c)4
-rw-r--r--drivers/mtd/nand/raw/cs553x_nand.c (renamed from drivers/mtd/nand/cs553x_nand.c)11
-rw-r--r--drivers/mtd/nand/raw/davinci_nand.c (renamed from drivers/mtd/nand/davinci_nand.c)5
-rw-r--r--drivers/mtd/nand/raw/denali.c (renamed from drivers/mtd/nand/denali.c)4
-rw-r--r--drivers/mtd/nand/raw/denali.h (renamed from drivers/mtd/nand/denali.h)0
-rw-r--r--drivers/mtd/nand/raw/denali_dt.c (renamed from drivers/mtd/nand/denali_dt.c)0
-rw-r--r--drivers/mtd/nand/raw/denali_pci.c (renamed from drivers/mtd/nand/denali_pci.c)0
-rw-r--r--drivers/mtd/nand/raw/diskonchip.c (renamed from drivers/mtd/nand/diskonchip.c)78
-rw-r--r--drivers/mtd/nand/raw/docg4.c (renamed from drivers/mtd/nand/docg4.c)4
-rw-r--r--drivers/mtd/nand/raw/fsl_elbc_nand.c (renamed from drivers/mtd/nand/fsl_elbc_nand.c)8
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_nand.c (renamed from drivers/mtd/nand/fsl_ifc_nand.c)6
-rw-r--r--drivers/mtd/nand/raw/fsl_upm.c (renamed from drivers/mtd/nand/fsl_upm.c)0
-rw-r--r--drivers/mtd/nand/raw/fsmc_nand.c (renamed from drivers/mtd/nand/fsmc_nand.c)252
-rw-r--r--drivers/mtd/nand/raw/gpio.c (renamed from drivers/mtd/nand/gpio.c)2
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/Makefile (renamed from drivers/mtd/nand/gpmi-nand/Makefile)0
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/bch-regs.h (renamed from drivers/mtd/nand/gpmi-nand/bch-regs.h)0
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c (renamed from drivers/mtd/nand/gpmi-nand/gpmi-lib.c)793
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c (renamed from drivers/mtd/nand/gpmi-nand/gpmi-nand.c)82
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h (renamed from drivers/mtd/nand/gpmi-nand/gpmi-nand.h)131
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h (renamed from drivers/mtd/nand/gpmi-nand/gpmi-regs.h)5
-rw-r--r--drivers/mtd/nand/raw/hisi504_nand.c (renamed from drivers/mtd/nand/hisi504_nand.c)4
-rw-r--r--drivers/mtd/nand/raw/jz4740_nand.c (renamed from drivers/mtd/nand/jz4740_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/jz4780_bch.c (renamed from drivers/mtd/nand/jz4780_bch.c)0
-rw-r--r--drivers/mtd/nand/raw/jz4780_bch.h (renamed from drivers/mtd/nand/jz4780_bch.h)0
-rw-r--r--drivers/mtd/nand/raw/jz4780_nand.c (renamed from drivers/mtd/nand/jz4780_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_mlc.c (renamed from drivers/mtd/nand/lpc32xx_mlc.c)0
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_slc.c (renamed from drivers/mtd/nand/lpc32xx_slc.c)0
-rw-r--r--drivers/mtd/nand/raw/marvell_nand.c (renamed from drivers/mtd/nand/marvell_nand.c)89
-rw-r--r--drivers/mtd/nand/raw/mpc5121_nfc.c (renamed from drivers/mtd/nand/mpc5121_nfc.c)9
-rw-r--r--drivers/mtd/nand/raw/mtk_ecc.c (renamed from drivers/mtd/nand/mtk_ecc.c)0
-rw-r--r--drivers/mtd/nand/raw/mtk_ecc.h (renamed from drivers/mtd/nand/mtk_ecc.h)0
-rw-r--r--drivers/mtd/nand/raw/mtk_nand.c (renamed from drivers/mtd/nand/mtk_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/mxc_nand.c (renamed from drivers/mtd/nand/mxc_nand.c)544
-rw-r--r--drivers/mtd/nand/raw/nand_amd.c (renamed from drivers/mtd/nand/nand_amd.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_base.c (renamed from drivers/mtd/nand/nand_base.c)335
-rw-r--r--drivers/mtd/nand/raw/nand_bbt.c (renamed from drivers/mtd/nand/nand_bbt.c)1
-rw-r--r--drivers/mtd/nand/raw/nand_bch.c (renamed from drivers/mtd/nand/nand_bch.c)12
-rw-r--r--drivers/mtd/nand/raw/nand_ecc.c (renamed from drivers/mtd/nand/nand_ecc.c)22
-rw-r--r--drivers/mtd/nand/raw/nand_hynix.c (renamed from drivers/mtd/nand/nand_hynix.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_ids.c (renamed from drivers/mtd/nand/nand_ids.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_macronix.c (renamed from drivers/mtd/nand/nand_macronix.c)13
-rw-r--r--drivers/mtd/nand/raw/nand_micron.c (renamed from drivers/mtd/nand/nand_micron.c)41
-rw-r--r--drivers/mtd/nand/raw/nand_samsung.c (renamed from drivers/mtd/nand/nand_samsung.c)0
-rw-r--r--drivers/mtd/nand/raw/nand_timings.c (renamed from drivers/mtd/nand/nand_timings.c)12
-rw-r--r--drivers/mtd/nand/raw/nand_toshiba.c (renamed from drivers/mtd/nand/nand_toshiba.c)26
-rw-r--r--drivers/mtd/nand/raw/nandsim.c (renamed from drivers/mtd/nand/nandsim.c)15
-rw-r--r--drivers/mtd/nand/raw/ndfc.c (renamed from drivers/mtd/nand/ndfc.c)0
-rw-r--r--drivers/mtd/nand/raw/nuc900_nand.c (renamed from drivers/mtd/nand/nuc900_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/omap2.c (renamed from drivers/mtd/nand/omap2.c)5
-rw-r--r--drivers/mtd/nand/raw/omap_elm.c (renamed from drivers/mtd/nand/omap_elm.c)0
-rw-r--r--drivers/mtd/nand/raw/orion_nand.c (renamed from drivers/mtd/nand/orion_nand.c)2
-rw-r--r--drivers/mtd/nand/raw/oxnas_nand.c (renamed from drivers/mtd/nand/oxnas_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/pasemi_nand.c (renamed from drivers/mtd/nand/pasemi_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/plat_nand.c (renamed from drivers/mtd/nand/plat_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/qcom_nandc.c (renamed from drivers/mtd/nand/qcom_nandc.c)4
-rw-r--r--drivers/mtd/nand/raw/r852.c (renamed from drivers/mtd/nand/r852.c)5
-rw-r--r--drivers/mtd/nand/raw/r852.h (renamed from drivers/mtd/nand/r852.h)9
-rw-r--r--drivers/mtd/nand/raw/s3c2410.c (renamed from drivers/mtd/nand/s3c2410.c)27
-rw-r--r--drivers/mtd/nand/raw/sh_flctl.c (renamed from drivers/mtd/nand/sh_flctl.c)10
-rw-r--r--drivers/mtd/nand/raw/sharpsl.c (renamed from drivers/mtd/nand/sharpsl.c)2
-rw-r--r--drivers/mtd/nand/raw/sm_common.c (renamed from drivers/mtd/nand/sm_common.c)5
-rw-r--r--drivers/mtd/nand/raw/sm_common.h (renamed from drivers/mtd/nand/sm_common.h)0
-rw-r--r--drivers/mtd/nand/raw/socrates_nand.c (renamed from drivers/mtd/nand/socrates_nand.c)2
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c (renamed from drivers/mtd/nand/sunxi_nand.c)155
-rw-r--r--drivers/mtd/nand/raw/tango_nand.c (renamed from drivers/mtd/nand/tango_nand.c)4
-rw-r--r--drivers/mtd/nand/raw/tmio_nand.c (renamed from drivers/mtd/nand/tmio_nand.c)0
-rw-r--r--drivers/mtd/nand/raw/txx9ndfmc.c (renamed from drivers/mtd/nand/txx9ndfmc.c)0
-rw-r--r--drivers/mtd/nand/raw/vf610_nfc.c (renamed from drivers/mtd/nand/vf610_nfc.c)677
-rw-r--r--drivers/mtd/nand/raw/xway_nand.c (renamed from drivers/mtd/nand/xway_nand.c)0
-rw-r--r--drivers/mtd/nftlmount.c8
-rw-r--r--drivers/mtd/ofpart.c18
-rw-r--r--drivers/mtd/rfd_ftl.c93
-rw-r--r--drivers/mtd/sm_ftl.c21
-rw-r--r--drivers/mtd/sm_ftl.h4
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c19
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c3
-rw-r--r--drivers/mtd/tests/mtd_test.c5
-rw-r--r--drivers/mtd/tests/pagetest.c10
-rw-r--r--drivers/mtd/tests/speedtest.c7
-rw-r--r--drivers/mtd/ubi/gluebi.c3
-rw-r--r--drivers/mtd/ubi/io.c36
-rw-r--r--drivers/net/ethernet/sfc/falcon/mtd.c11
-rw-r--r--drivers/net/ethernet/sfc/mtd.c11
-rw-r--r--drivers/staging/goldfish/goldfish_nand.c3
-rw-r--r--drivers/staging/mt29f_spinand/mt29f_spinand.c4
-rw-r--r--fs/jffs2/erase.c37
-rw-r--r--include/linux/mtd/bbm.h2
-rw-r--r--include/linux/mtd/mtd.h19
-rw-r--r--include/linux/mtd/nand.h731
-rw-r--r--include/linux/mtd/nand_ecc.h2
-rw-r--r--include/linux/mtd/ndfc.h2
-rw-r--r--include/linux/mtd/partitions.h1
-rw-r--r--include/linux/mtd/rawnand.h106
170 files changed, 3704 insertions, 4372 deletions
diff --git a/.mailmap b/.mailmap
index dc42cfb41304..9795e6bafe9b 100644
--- a/.mailmap
+++ b/.mailmap
@@ -34,9 +34,9 @@ Axel Lin <axel.lin@gmail.com>
Ben Gardner <bgardner@wabtec.com>
Ben M Cahill <ben.m.cahill@intel.com>
Björn Steinbrink <B.Steinbrink@gmx.de>
-Boris Brezillon <boris.brezillon@free-electrons.com>
-Boris Brezillon <boris.brezillon@free-electrons.com> <b.brezillon.dev@gmail.com>
-Boris Brezillon <boris.brezillon@free-electrons.com> <b.brezillon@overkiz.com>
+Boris Brezillon <boris.brezillon@bootlin.com> <boris.brezillon@free-electrons.com>
+Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon.dev@gmail.com>
+Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon@overkiz.com>
Brian Avery <b.avery@hp.com>
Brian King <brking@us.ibm.com>
Christoph Hellwig <hch@lst.de>
@@ -128,6 +128,7 @@ Mayuresh Janorkar <mayur@ti.com>
Michael Buesch <m@bues.ch>
Michel Dänzer <michel@tungstengraphics.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>
Mohit Kumar <mohit.kumar@st.com> <mohit.kumar.dhaka@gmail.com>
Morten Welinder <terra@gnome.org>
diff --git a/Documentation/arm/Samsung-S3C24XX/S3C2412.txt b/Documentation/arm/Samsung-S3C24XX/S3C2412.txt
index f057876b920b..dc1fd362d3c1 100644
--- a/Documentation/arm/Samsung-S3C24XX/S3C2412.txt
+++ b/Documentation/arm/Samsung-S3C24XX/S3C2412.txt
@@ -46,7 +46,7 @@ NAND
----
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
diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
index 63d4d626fbd5..483e9cfac1b1 100644
--- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
+++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
@@ -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>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/mtd/marvell-nand.txt b/Documentation/devicetree/bindings/mtd/marvell-nand.txt
index c08fb477b3c6..e0c790706b9b 100644
--- a/Documentation/devicetree/bindings/mtd/marvell-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/marvell-nand.txt
@@ -14,7 +14,10 @@ Required properties:
- #address-cells: shall be set to 1. Encode the NAND CS.
- #size-cells: shall be set to 0.
- 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
NAND controller related registers (only required with the
"marvell,armada-8k-nand[-controller]" compatibles).
diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt
index 4a0a48bf4ecb..232fa12e90ef 100644
--- a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt
+++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt
@@ -41,6 +41,13 @@ additional (optional) property is defined:
- 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
address space. See partition.txt for more detail.
diff --git a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt
index 5e13a5cdff03..0734f03bf3d3 100644
--- a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt
@@ -24,8 +24,8 @@ Optional properties:
- allwinner,rb : shall contain the native Ready/Busy ids.
or
- rb-gpios : shall contain the gpios used as R/B pins.
-- nand-ecc-mode : one of the supported ECC modes ("hw", "hw_syndrome", "soft",
- "soft_bch" or "none")
+- nand-ecc-mode : one of the supported ECC modes ("hw", "soft", "soft_bch" or
+ "none")
see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
diff --git a/Documentation/driver-api/gpio/drivers-on-gpio.rst b/Documentation/driver-api/gpio/drivers-on-gpio.rst
index 019483868977..7da0c1dd1f7a 100644
--- a/Documentation/driver-api/gpio/drivers-on-gpio.rst
+++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst
@@ -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"
periodically, it will reset the system.
-- gpio-nand: drivers/mtd/nand/gpio.c is used to connect a NAND flash chip to
- a set of simple GPIO lines: RDY, NCE, ALE, CLE, NWP. It interacts with the
+- gpio-nand: drivers/mtd/nand/raw/gpio.c is used to connect a NAND flash chip
+ 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
any other NAND driving hardware.
diff --git a/Documentation/driver-api/mtdnand.rst b/Documentation/driver-api/mtdnand.rst
index 2a5191b6d445..dcd63599f700 100644
--- a/Documentation/driver-api/mtdnand.rst
+++ b/Documentation/driver-api/mtdnand.rst
@@ -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
hints" for an explanation.
-.. kernel-doc:: drivers/mtd/nand/nand_base.c
+.. kernel-doc:: drivers/mtd/nand/raw/nand_base.c
:export:
-.. kernel-doc:: drivers/mtd/nand/nand_ecc.c
+.. kernel-doc:: drivers/mtd/nand/raw/nand_ecc.c
:export:
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
relevant for a board driver developer.
-.. kernel-doc:: drivers/mtd/nand/nand_base.c
+.. kernel-doc:: drivers/mtd/nand/raw/nand_base.c
:internal:
-.. kernel-doc:: drivers/mtd/nand/nand_bbt.c
+.. kernel-doc:: drivers/mtd/nand/raw/nand_bbt.c
:internal:
Credits
diff --git a/MAINTAINERS b/MAINTAINERS
index c6973001bf3c..881d328020df 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1238,7 +1238,7 @@ F: arch/arm/boot/dts/aspeed-*
F: drivers/*/*aspeed*
ARM/ATMEL AT91 Clock Support
-M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Boris Brezillon <boris.brezillon@bootlin.com>
S: Maintained
F: drivers/clk/at91
@@ -1721,7 +1721,7 @@ F: drivers/input/keyboard/w90p910_keypad.c
F: drivers/input/touchscreen/w90p910_ts.c
F: drivers/watchdog/nuc900_wdt.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/spi/spi-nuc900.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: bcm-kernel-feedback-list@broadcom.com
S: Maintained
-F: drivers/mtd/nand/brcmnand/
+F: drivers/mtd/nand/raw/brcmnand/
BROADCOM STB DPFE DRIVER
M: Markus Mayer <mmayer@broadcom.com>
@@ -4091,7 +4091,7 @@ DENALI NAND DRIVER
M: Masahiro Yamada <yamada.masahiro@socionext.com>
L: linux-mtd@lists.infradead.org
S: Supported
-F: drivers/mtd/nand/denali*
+F: drivers/mtd/nand/raw/denali*
DESIGNWARE USB2 DRD IP DRIVER
M: Minas Harutyunyan <hminas@synopsys.com>
@@ -4633,7 +4633,7 @@ F: Documentation/gpu/meson.rst
T: git git://anongit.freedesktop.org/drm/drm-misc
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
S: Supported
F: drivers/gpu/drm/atmel-hlcdc/
@@ -5630,7 +5630,7 @@ FREESCALE GPMI NAND DRIVER
M: Han Xu <han.xu@nxp.com>
L: linux-mtd@lists.infradead.org
S: Maintained
-F: drivers/mtd/nand/gpmi-nand/*
+F: drivers/mtd/nand/raw/gpmi-nand/*
FREESCALE I2C CPM DRIVER
M: Jochen Friedrich <jochen@scram.de>
@@ -6943,7 +6943,7 @@ INGENIC JZ4780 NAND DRIVER
M: Harvey Hunt <harveyhuntnexus@gmail.com>
L: linux-mtd@lists.infradead.org
S: Maintained
-F: drivers/mtd/nand/jz4780_*
+F: drivers/mtd/nand/raw/jz4780_*
INOTIFY
M: Jan Kara <jack@suse.cz>
@@ -8431,7 +8431,7 @@ F: include/uapi/drm/armada_drm.h
F: Documentation/devicetree/bindings/display/armada/
MARVELL CRYPTO DRIVER
-M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Boris Brezillon <boris.brezillon@bootlin.com>
M: Arnaud Ebalard <arno@natisbad.org>
F: drivers/crypto/marvell/
S: Maintained
@@ -8490,10 +8490,10 @@ S: Odd Fixes
F: drivers/net/wireless/marvell/mwl8k.c
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
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
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
@@ -9098,10 +9098,9 @@ F: mm/
MEMORY TECHNOLOGY DEVICES (MTD)
M: David Woodhouse <dwmw2@infradead.org>
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: Richard Weinberger <richard@nod.at>
-M: Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
L: linux-mtd@lists.infradead.org
W: http://www.linux-mtd.infradead.org/
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>
L: linux-mtd@lists.infradead.org
S: Supported
-F: drivers/mtd/nand/atmel/*
+F: drivers/mtd/nand/raw/atmel/*
F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
@@ -9518,7 +9517,7 @@ S: Supported
F: drivers/net/ethernet/myricom/myri10ge/
NAND FLASH SUBSYSTEM
-M: Boris Brezillon <boris.brezillon@free-electrons.com>
+M: Boris Brezillon <boris.brezillon@bootlin.com>
R: Richard Weinberger <richard@nod.at>
L: linux-mtd@lists.infradead.org
W: http://www.linux-mtd.infradead.org/
@@ -10312,7 +10311,7 @@ ONENAND FLASH DRIVER
M: Kyungmin Park <kyungmin.park@samsung.com>
L: linux-mtd@lists.infradead.org
S: Maintained
-F: drivers/mtd/onenand/
+F: drivers/mtd/nand/onenand/
F: include/linux/mtd/onenand*.h
ONSTREAM SCSI TAPE DRIVER
@@ -11921,8 +11920,8 @@ F: drivers/memstick/host/r592.*
RICOH SMARTMEDIA/XD DRIVER
M: Maxim Levitsky <maximlevitsky@gmail.com>
S: Maintained
-F: drivers/mtd/nand/r852.c
-F: drivers/mtd/nand/r852.h
+F: drivers/mtd/nand/raw/r852.c
+F: drivers/mtd/nand/raw/r852.h
RISC-V ARCHITECTURE
M: Palmer Dabbelt <palmer@sifive.com>
@@ -13118,7 +13117,6 @@ F: arch/arm/boot/dts/spear*
F: arch/arm/mach-spear/
SPI NOR SUBSYSTEM
-M: Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
M: Marek Vasut <marek.vasut@gmail.com>
L: linux-mtd@lists.infradead.org
W: http://www.linux-mtd.infradead.org/
@@ -14762,7 +14760,7 @@ VF610 NAND DRIVER
M: Stefan Agner <stefan@agner.ch>
L: linux-mtd@lists.infradead.org
S: Supported
-F: drivers/mtd/nand/vf610_nfc.c
+F: drivers/mtd/nand/raw/vf610_nfc.c
VFAT/FAT/MSDOS FILESYSTEM
M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 2a8ac6829d42..46ab7feec6b6 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -333,8 +333,6 @@ source "drivers/mtd/devices/Kconfig"
source "drivers/mtd/nand/Kconfig"
-source "drivers/mtd/onenand/Kconfig"
-
source "drivers/mtd/lpddr/Kconfig"
source "drivers/mtd/spi-nor/Kconfig"
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index d6f8f625e1ff..93473d215a38 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -32,7 +32,7 @@ obj-$(CONFIG_MTD_SWAP) += mtdswap.o
nftl-objs := nftlcore.o nftlmount.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_UBI) += ubi/
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 5e1b68cbcd0a..d4c07b85f18e 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -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)
{
- unsigned long ofs, len;
- int ret;
-
- 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;
+ return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr,
+ instr->len, NULL);
}
static void cfi_intelext_sync (struct mtd_info *mtd)
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 56aa6b75213d..668e2cbc155b 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -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)
{
- unsigned long ofs, len;
- int ret;
-
- 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;
+ return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr,
+ instr->len, NULL);
}
@@ -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 cfi_private *cfi = map->fldrv_priv;
- int ret = 0;
if (instr->addr != 0)
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)
return -EINVAL;
- ret = do_erase_chip(map, &cfi->chips[0]);
- if (ret)
- return ret;
-
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
- return 0;
+ return do_erase_chip(map, &cfi->chips[0]);
}
static int do_atmel_lock(struct map_info *map, struct flchip *chip,
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c
index 7d342965f392..7b7658a05036 100644
--- a/drivers/mtd/chips/cfi_cmdset_0020.c
+++ b/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -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;
}
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index b479bd81120b..6f7e7e1b3fe5 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -53,6 +53,8 @@
#define AT49BV32XT 0x00C9
/* Eon */
+#define EN29LV400AT 0x22B9
+#define EN29LV400AB 0x22BA
#define EN29SL800BB 0x226B
#define EN29SL800BT 0x22EA
@@ -643,6 +645,36 @@ static const struct amd_flash_info jedec_table[] = {
}
}, {
.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,
.dev_id = EN29SL800BT,
.name = "Eon EN29SL800BT",
.devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c
index 1cd0fff0e940..c37fce926864 100644
--- a/drivers/mtd/chips/map_ram.c
+++ b/drivers/mtd/chips/map_ram.c
@@ -131,8 +131,6 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
allff = map_word_ff(map);
for (i=0; i<instr->len; i += map_bankwidth(map))
map_write(map, allff, instr->addr + i);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c
index e2bd81817df4..9baa81b8780c 100644
--- a/drivers/mtd/devices/bcm47xxsflash.c
+++ b/drivers/mtd/devices/bcm47xxsflash.c
@@ -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)
{
struct bcm47xxsflash *b47s = mtd->priv;
- int err;
switch (b47s->type) {
case BCM47XXSFLASH_TYPE_ST:
@@ -89,16 +88,7 @@ static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase)
break;
}
- err = 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;
+ return bcm47xxsflash_poll(b47s, HZ);
}
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index 62fd6905c648..c9e424993e37 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -88,17 +88,12 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
size_t len = instr->len;
int err;
- instr->state = MTD_ERASING;
mutex_lock(&dev->write_mutex);
err = _block2mtd_erase(dev, from, len);
mutex_unlock(&dev->write_mutex);
- if (err) {
+ if (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;
}
@@ -225,7 +220,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size,
int i;
#endif
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;
char *name;
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index a85af236b44d..c594fe5eac08 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1191,39 +1191,27 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
{
struct docg3 *docg3 = mtd->priv;
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);
- info->state = MTD_ERASE_PENDING;
calc_block_sector(info->addr + info->len, &block0, &block1, &page,
&ofs, docg3->reliable);
- ret = -EINVAL;
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,
docg3->reliable);
mutex_lock(&docg3->cascade->lock);
doc_set_device_id(docg3, docg3->device_id);
doc_set_reliable_mode(docg3);
for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
- info->state = MTD_ERASING;
ret = doc_erase_block(docg3, block0, block1);
block0 += 2;
block1 += 2;
}
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;
}
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c
index 555b94406e0b..f67b653c17d7 100644
--- a/drivers/mtd/devices/lart.c
+++ b/drivers/mtd/devices/lart.c
@@ -414,10 +414,7 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
while (len)
{
if (!erase_block (addr))
- {
- instr->state = MTD_ERASE_FAILED;
return (-EIO);
- }
addr += 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++;
}
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return (0);
}
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 5dc8bd042cc5..aaaeaae01e1d 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -220,10 +220,6 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
}
mutex_unlock(&priv->lock);
- /* Inform MTD subsystem that erase is complete */
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return 0;
}
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index 0bf4aeaf0cb8..46238796145f 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -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))
return -EINVAL;
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
+
return 0;
}
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
index 7287696a21f9..9ee04b5f9311 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -39,13 +39,6 @@ static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
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;
}
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c
index cadea0620cd0..5d842cbca3de 100644
--- a/drivers/mtd/devices/pmc551.c
+++ b/drivers/mtd/devices/pmc551.c
@@ -184,12 +184,10 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
}
out:
- instr->state = MTD_ERASE_DONE;
#ifdef CONFIG_MTD_PMC551_DEBUG
printk(KERN_DEBUG "pmc551_erase() done\n");
#endif
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/devices/powernv_flash.c b/drivers/mtd/devices/powernv_flash.c
index 26f9feaa5d17..c1312b141ae0 100644
--- a/drivers/mtd/devices/powernv_flash.c
+++ b/drivers/mtd/devices/powernv_flash.c
@@ -175,19 +175,11 @@ static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)
{
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,
erase->len, NULL, NULL);
-
- if (rc) {
+ if (rc)
erase->fail_addr = erase->addr;
- erase->state = MTD_ERASE_FAILED;
- } else {
- erase->state = MTD_ERASE_DONE;
- }
- mtd_erase_callback(erase);
+
return rc;
}
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c
index 0ec85f316d24..10183ee4e12b 100644
--- a/drivers/mtd/devices/slram.c
+++ b/drivers/mtd/devices/slram.c
@@ -84,12 +84,7 @@ static int slram_erase(struct mtd_info *mtd, struct erase_info *instr)
slram_priv_t *priv = mtd->priv;
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);
}
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
index ddf478976013..986f81d2f93e 100644
--- a/drivers/mtd/devices/spear_smi.c
+++ b/drivers/mtd/devices/spear_smi.c
@@ -518,7 +518,6 @@ static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
/* preparing the command for flash */
ret = spear_smi_erase_sector(dev, bank, command, 4);
if (ret) {
- e_info->state = MTD_ERASE_FAILED;
mutex_unlock(&flash->lock);
return ret;
}
@@ -527,8 +526,6 @@ static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
}
mutex_unlock(&flash->lock);
- e_info->state = MTD_ERASE_DONE;
- mtd_erase_callback(e_info);
return 0;
}
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c
index 5b84d71efb36..1897f33fe3e7 100644
--- a/drivers/mtd/devices/sst25l.c
+++ b/drivers/mtd/devices/sst25l.c
@@ -195,7 +195,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
err = sst25l_erase_sector(flash, addr);
if (err) {
mutex_unlock(&flash->lock);
- instr->state = MTD_ERASE_FAILED;
dev_err(&flash->spi->dev, "Erase failed\n");
return err;
}
@@ -205,8 +204,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
mutex_unlock(&flash->lock);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
index 7bc29d725200..55d4a77f3b7f 100644
--- a/drivers/mtd/devices/st_spi_fsm.c
+++ b/drivers/mtd/devices/st_spi_fsm.c
@@ -1825,13 +1825,9 @@ static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
mutex_unlock(&fsm->lock);
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return 0;
out1:
- instr->state = MTD_ERASE_FAILED;
mutex_unlock(&fsm->lock);
return ret;
@@ -1868,8 +1864,7 @@ static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm)
*/
ext_jedec = id[3] << 8 | id[4];
- dev_dbg(fsm->dev, "JEDEC = 0x%08x [%02x %02x %02x %02x %02x]\n",
- jedec, id[0], id[1], id[2], id[3], id[4]);
+ dev_dbg(fsm->dev, "JEDEC = 0x%08x [%5ph]\n", jedec, id);
for (info = flash_types; info->name; info++) {
if (info->jedec_id == jedec) {
diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
index 664d206a4cbe..ef6ad2551d57 100644
--- a/drivers/mtd/ftl.c
+++ b/drivers/mtd/ftl.c
@@ -140,12 +140,6 @@ typedef struct partition_t {
#define XFER_PREPARED 0x03
#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
@@ -348,18 +342,19 @@ static int erase_xfer(partition_t *part,
if (!erase)
return -ENOMEM;
- erase->mtd = part->mbd.mtd;
- erase->callback = ftl_erase_callback;
erase->addr = xfer->Offset;
erase->len = 1 << part->header.EraseUnitSize;
- erase->priv = (u_long)part;
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)
- xfer->EraseCount++;
- else
- kfree(erase);
+ kfree(erase);
return ret;
} /* 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)
{
erase_unit_header_t header;
@@ -429,8 +393,8 @@ static int prepare_xfer(partition_t *part, int i)
}
/* Write the BAM stub */
- nbam = (part->BlocksPerUnit * sizeof(uint32_t) +
- le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
+ le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
ctl = cpu_to_le32(BLOCK_CONTROL);
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c
index 8d6bb189ea8e..aab4f68bd36f 100644
--- a/drivers/mtd/inftlmount.c
+++ b/drivers/mtd/inftlmount.c
@@ -208,8 +208,6 @@ static int find_boot_record(struct INFTLrecord *inftl)
if (ip->Reserved0 != ip->firstUnit) {
struct erase_info *instr = &inftl->instr;
- instr->mtd = inftl->mbd.mtd;
-
/*
* Most likely this is using the
* undocumented qiuck mount feature.
@@ -385,7 +383,6 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
_first_? */
/* Use async erase interface, test return code */
- instr->mtd = inftl->mbd.mtd;
instr->addr = block * inftl->EraseSize;
instr->len = inftl->mbd.mtd->erasesize;
/* 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. */
for (physblock = 0; physblock < inftl->EraseSize;
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",
block);
goto fail;
diff --git a/drivers/mtd/lpddr/lpddr2_nvm.c b/drivers/mtd/lpddr/lpddr2_nvm.c
index 2342277c9bcb..5d73db2a496d 100644
--- a/drivers/mtd/lpddr/lpddr2_nvm.c
+++ b/drivers/mtd/lpddr/lpddr2_nvm.c
@@ -380,14 +380,8 @@ out:
*/
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,
- LPDDR2_NVM_ERASE);
- if (!ret) {
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
- }
-
- return ret;
+ return lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len,
+ LPDDR2_NVM_ERASE);
}
/*
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c
index 018c75faadb3..5c5ba3c7c79d 100644
--- a/drivers/mtd/lpddr/lpddr_cmds.c
+++ b/drivers/mtd/lpddr/lpddr_cmds.c
@@ -693,8 +693,6 @@ static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr)
ofs += size;
len -= size;
}
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
}
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 542fdf8e81fa..bdc1283f30fb 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -334,16 +334,6 @@ config MTD_PCMCIA_ANONYMOUS
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
tristate "GPIO-assisted Flash Chip Support"
depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index b849aaf85c34..51acf1fec19b 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -42,7 +42,6 @@ obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.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_VMU) += vmu-flash.o
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c
deleted file mode 100644
index 41730feeace8..000000000000
--- a/drivers/mtd/maps/bfin-async-flash.c
+++ /dev/null
@@ -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");
diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c
index b1bd4faecfb2..527b1682381f 100644
--- a/drivers/mtd/maps/physmap_of_core.c
+++ b/drivers/mtd/maps/physmap_of_core.c
@@ -20,6 +20,7 @@
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>
+#include <linux/mtd/cfi_endian.h>
#include <linux/of.h>
#include <linux/of_address.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.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);
if (err)
goto err_out;
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index bb4c14f83c75..a5b1933c0490 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -55,48 +55,27 @@ struct mtdblk_dev {
* 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,
int len, const char *buf)
{
struct erase_info erase;
- DECLARE_WAITQUEUE(wait, current);
- wait_queue_head_t wait_q;
size_t retlen;
int ret;
/*
* First, let's erase the flash block.
*/
-
- init_waitqueue_head(&wait_q);
- erase.mtd = mtd;
- erase.callback = erase_callback;
erase.addr = pos;
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);
if (ret) {
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&wait_q, &wait);
printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
"on \"%s\" failed\n",
pos, len, mtd->name);
return ret;
}
- schedule(); /* Wait for erase to finish. */
- remove_wait_queue(&wait_q, &wait);
-
/*
* Next, write the data to flash.
*/
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 7d80a8bb96fe..cd67c85cc87d 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -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.
======================================================================*/
-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)
{
@@ -709,11 +705,6 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
if (!erase)
ret = -ENOMEM;
else {
- wait_queue_head_t waitq;
- DECLARE_WAITQUEUE(wait, current);
-
- init_waitqueue_head(&waitq);
-
if (cmd == MEMERASE64) {
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->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);
- 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);
}
break;
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index 60bf53df5454..6b86d1a73cf2 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -333,45 +333,6 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
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)
{
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;
length -= erase->len;
- if ((err = concat_dev_erase(subdev, erase))) {
+ if ((err = mtd_erase(subdev, erase))) {
/* sanity check: should never happen since
* block alignment has been checked above */
BUG_ON(err == -EINVAL);
@@ -485,14 +446,9 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
erase->addr = 0;
offset += subdev->size;
}
- instr->state = erase->state;
kfree(erase);
- if (err)
- return err;
- if (instr->callback)
- instr->callback(instr);
- return 0;
+ return err;
}
static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 28553c840d32..807d17d863b3 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -419,7 +419,7 @@ int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
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
* @info: pairing information struct
*
@@ -641,29 +641,6 @@ out_error:
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
* 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
* 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
* is used, see 'parse_mtd_partitions()' for more information). If none are
* found this functions tries to fallback to information specified in
* @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
* @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,
int nr_parts)
{
- struct mtd_partitions parsed;
+ struct mtd_partitions parsed = { };
int ret;
mtd_set_dev_defaults(mtd);
- memset(&parsed, 0, sizeof(parsed));
+ if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
+ ret = add_mtd_device(mtd);
+ if (ret)
+ return ret;
+ }
+ /* Prefer parsed partitions over driver-provided fallback */
ret = parse_mtd_partitions(mtd, types, &parsed, parser_data);
- if ((ret < 0 || parsed.nr_parts == 0) && parts && nr_parts) {
- /* 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));
+ if (!ret && parsed.nr_parts) {
+ parts = parsed.parts;
+ nr_parts = parsed.nr_parts;
}
- ret = mtd_add_device_partitions(mtd, &parsed);
+ 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)
goto out;
@@ -758,6 +736,9 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
out:
/* Cleanup any parsed partitions */
mtd_part_parser_cleanup(&parsed);
+ if (ret && device_is_registered(&mtd->dev))
+ del_mtd_device(mtd);
+
return ret;
}
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);
/*
- * Erase is an asynchronous operation. Device drivers are supposed
- * to call instr->callback() whenever the operation completes, even
- * if it completes with a failure.
- * Callers are supposed to pass a callback function and wait for it
- * to be called before writing to the block.
+ * Erase is an synchronous operation. Device drivers are epected to return a
+ * negative error code if the operation failed and update instr->fail_addr
+ * to point the portion that was not properly erased.
*/
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)
return -EINVAL;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
- instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- if (!instr->len) {
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
+
+ if (!instr->len)
return 0;
- }
+
ledtrig_mtd_activity();
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);
/**
- * 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
- * @eccbuf: source buffer to get data bytes from
+ * @databuf: source buffer to get data bytes from
* @oobbuf: OOB buffer
* @start: first ECC byte 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);
/**
- * 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
*
* Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index 97bb8f6304d4..9f25111fd559 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -84,12 +84,6 @@ static int page_is_used(struct mtdoops_context *cxt, int page)
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)
{
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 erase_pages = mtd->erasesize / record_size;
struct erase_info erase;
- DECLARE_WAITQUEUE(wait, current);
- wait_queue_head_t wait_q;
int ret;
int page;
- init_waitqueue_head(&wait_q);
- erase.mtd = mtd;
- erase.callback = mtdoops_erase_callback;
erase.addr = offset;
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);
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",
(unsigned long long)erase.addr,
(unsigned long long)erase.len, mtddev);
return ret;
}
- schedule(); /* Wait for erase to finish. */
- remove_wait_queue(&wait_q, &wait);
-
/* Mark pages as unused */
for (page = start_page; page < start_page + erase_pages; page++)
mark_page_unused(cxt, page);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 76cd21d1171b..023516a63276 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -30,6 +30,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/err.h>
+#include <linux/of.h>
#include "mtdcore.h"
@@ -205,27 +206,12 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
instr->addr += part->offset;
ret = part->parent->_erase(part->parent, instr);
- if (ret) {
- if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
- instr->fail_addr -= part->offset;
- instr->addr -= part->offset;
- }
- 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->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
- instr->fail_addr -= part->offset;
- instr->addr -= part->offset;
- }
- if (instr->callback)
- instr->callback(instr);
+ return ret;
}
-EXPORT_SYMBOL_GPL(mtd_erase_callback);
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
@@ -861,6 +847,92 @@ static int mtd_part_do_parse(struct mtd_part_parser *parser,
}
/**
+ * 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
* @master: the master partition (describes whole MTD device)
* @types: names of partition parsers to try or %NULL
@@ -892,19 +964,30 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
types = default_mtd_part_types;
for ( ; *types; types++) {
- pr_debug("%s: parsing partitions %s\n", master->name, *types);
- parser = mtd_part_parser_get(*types);
- if (!parser && !request_module("%s", *types))
+ /*
+ * ofpart is a special type that means OF partitioning info
+ * 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);
- pr_debug("%s: got parser %s\n", master->name,
- parser ? parser->name : NULL);
- if (!parser)
- continue;
- ret = mtd_part_do_parse(parser, master, pparts, data);
+ if (!parser && !request_module("%s", *types))
+ parser = mtd_part_parser_get(*types);
+ pr_debug("%s: got parser %s\n", master->name,
+ parser ? parser->name : NULL);
+ if (!parser)
+ continue;
+ ret = mtd_part_do_parse(parser, master, pparts, data);
+ if (ret <= 0)
+ mtd_part_parser_put(parser);
+ }
/* Found partitions! */
if (ret > 0)
return 0;
- mtd_part_parser_put(parser);
/*
* Stash the first error we see; only report it if no parser
* succeeds
diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
index 7eb0e1f4f980..7161f8a17f62 100644
--- a/drivers/mtd/mtdswap.c
+++ b/drivers/mtd/mtdswap.c
@@ -536,18 +536,10 @@ static void mtdswap_store_eb(struct mtdswap_dev *d, struct swap_eb *eb)
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)
{
struct mtd_info *mtd = d->mtd;
struct erase_info erase;
- wait_queue_head_t wq;
unsigned int retries = 0;
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;
retry:
- init_waitqueue_head(&wq);
memset(&erase, 0, sizeof(struct erase_info));
-
- erase.mtd = mtd;
- erase.callback = mtdswap_erase_callback;
erase.addr = mtdswap_eb_offset(d, eb);
erase.len = mtd->erasesize;
- erase.priv = (u_long)&wq;
ret = mtd_erase(mtd, &erase);
if (ret) {
@@ -582,27 +569,6 @@ retry:
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;
}
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 605ec8cce67b..88c7d3b4ff8b 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,569 +1,6 @@
-config MTD_NAND_ECC
+config MTD_NAND_CORE
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.
+source "drivers/mtd/nand/onenand/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
+source "drivers/mtd/nand/raw/Kconfig"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index c882d5ef192a..3f0cb87f1a57 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -1,70 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
-#
-# linux/drivers/nand/Makefile
-#
-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
+nandcore-objs := core.o bbt.o
+obj-$(CONFIG_MTD_NAND_CORE) += nandcore.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_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
+obj-y += onenand/
+obj-y += raw/
diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c
new file mode 100644
index 000000000000..56cde38b92c0
--- /dev/null
+++ b/drivers/mtd/nand/bbt.c
@@ -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);
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
deleted file mode 100644
index 87bbd177b3e5..000000000000
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ /dev/null
@@ -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);
diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
new file mode 100644
index 000000000000..d0cd6f8635d7
--- /dev/null
+++ b/drivers/mtd/nand/core.c
@@ -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");
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/nand/onenand/Kconfig
index 9dc15748947b..9dc15748947b 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/nand/onenand/Kconfig
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/nand/onenand/Makefile
index f8b624aca9cc..f8b624aca9cc 100644
--- a/drivers/mtd/onenand/Makefile
+++ b/drivers/mtd/nand/onenand/Makefile
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/nand/onenand/generic.c
index 125da34d8ff9..d5ccaf943b91 100644
--- a/drivers/mtd/onenand/generic.c
+++ b/drivers/mtd/nand/onenand/generic.c
@@ -1,6 +1,4 @@
/*
- * linux/drivers/mtd/onenand/generic.c
- *
* Copyright (c) 2005 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com>
*
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/nand/onenand/omap2.c
index 87c34f607a75..9c159f0dd9a6 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/nand/onenand/omap2.c
@@ -1,6 +1,4 @@
/*
- * linux/drivers/mtd/onenand/omap2.c
- *
* OneNAND driver for OMAP2 / OMAP3
*
* Copyright © 2005-2006 Nokia Corporation
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c
index 979f4031f23c..b7105192cb12 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/nand/onenand/onenand_base.c
@@ -1,6 +1,4 @@
/*
- * linux/drivers/mtd/onenand/onenand_base.c
- *
* Copyright © 2005-2009 Samsung Electronics
* Copyright © 2007 Nokia Corporation
*
@@ -2143,7 +2141,6 @@ static int onenand_multiblock_erase_verify(struct mtd_info *mtd,
if (ret) {
printk(KERN_ERR "%s: Failed verify, block %d\n",
__func__, onenand_block(this, addr));
- instr->state = MTD_ERASE_FAILED;
instr->fail_addr = addr;
return -1;
}
@@ -2172,8 +2169,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
int ret = 0;
int bdry_block = 0;
- instr->state = MTD_ERASING;
-
if (ONENAND_IS_DDP(this)) {
loff_t bdry_addr = this->chipsize >> 1;
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 "
"at addr 0x%012llx\n",
__func__, (unsigned long long) addr);
- instr->state = MTD_ERASE_FAILED;
return -EIO;
}
len -= block_size;
@@ -2227,7 +2221,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
printk(KERN_ERR "%s: Failed multiblock erase, "
"block %d\n", __func__,
onenand_block(this, addr));
- instr->state = MTD_ERASE_FAILED;
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
return -EIO;
}
@@ -2247,7 +2240,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
if (ret) {
printk(KERN_ERR "%s: Failed erase, block %d\n",
__func__, onenand_block(this, addr));
- instr->state = MTD_ERASE_FAILED;
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
return -EIO;
}
@@ -2259,7 +2251,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
/* verify */
verify_instr.len = eb_count * block_size;
if (onenand_multiblock_erase_verify(mtd, &verify_instr)) {
- instr->state = verify_instr.state;
instr->fail_addr = verify_instr.fail_addr;
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;
}
- instr->state = MTD_ERASING;
-
/* Loop through the blocks */
while (len) {
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 "
"at addr 0x%012llx\n",
__func__, (unsigned long long) addr);
- instr->state = MTD_ERASE_FAILED;
return -EIO;
}
@@ -2318,7 +2306,6 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd,
if (ret) {
printk(KERN_ERR "%s: Failed erase, block %d\n",
__func__, onenand_block(this, addr));
- instr->state = MTD_ERASE_FAILED;
instr->fail_addr = addr;
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 */
onenand_release_device(mtd);
- /* Do call back function */
- if (!ret) {
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
- }
-
return ret;
}
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/nand/onenand/onenand_bbt.c
index 420260c25ca0..dde20487937d 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/nand/onenand/onenand_bbt.c
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * linux/drivers/mtd/onenand/onenand_bbt.c
- *
* Bad Block Table support for the OneNAND driver
*
* Copyright(c) 2005 Samsung Electronics
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/nand/onenand/samsung.c
index 2e9d076e445a..2e9d076e445a 100644
--- a/drivers/mtd/onenand/samsung.c
+++ b/drivers/mtd/nand/onenand/samsung.c
diff --git a/drivers/mtd/onenand/samsung.h b/drivers/mtd/nand/onenand/samsung.h
index 9016dc0136a8..9016dc0136a8 100644
--- a/drivers/mtd/onenand/samsung.h
+++ b/drivers/mtd/nand/onenand/samsung.h
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
new file mode 100644
index 000000000000..19a2b283fbbe
--- /dev/null
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -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
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
new file mode 100644
index 000000000000..165b7ef9e9a1
--- /dev/null
+++ b/drivers/mtd/nand/raw/Makefile
@@ -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
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index d60ada45c549..37a3cc21c7bc 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -1,11 +1,12 @@
/*
- * drivers/mtd/nand/ams-delta.c
- *
* 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>
- * 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
* 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 */
this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
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;
goto out;
}
@@ -219,7 +220,7 @@ static int ams_delta_init(struct platform_device *pdev)
this->dev_ready = ams_delta_nand_ready;
} else {
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 */
this->chip_delay = 30;
diff --git a/drivers/mtd/nand/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile
index 288db4f38a8f..288db4f38a8f 100644
--- a/drivers/mtd/nand/atmel/Makefile
+++ b/drivers/mtd/nand/raw/atmel/Makefile
diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index b2f00b398490..12f6753d47ae 100644
--- a/drivers/mtd/nand/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -9,10 +9,10 @@
*
* 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)
*
- * Derived from drivers/mtd/spia.c
+ * Derived from drivers/mtd/spia.c (removed in v3.8)
* Copyright 2000 Steven J. Hill (sjhill@cotw.com)
*
*
diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c
index ca0a70389ba9..555a74e15269 100644
--- a/drivers/mtd/nand/atmel/pmecc.c
+++ b/drivers/mtd/nand/raw/atmel/pmecc.c
@@ -9,10 +9,10 @@
*
* 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)
*
- * Derived from drivers/mtd/spia.c
+ * Derived from drivers/mtd/spia.c (removed in v3.8)
* Copyright 2000 Steven J. Hill (sjhill@cotw.com)
*
* Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
diff --git a/drivers/mtd/nand/atmel/pmecc.h b/drivers/mtd/nand/raw/atmel/pmecc.h
index 817e0dd9fd15..808f1be0d6ad 100644
--- a/drivers/mtd/nand/atmel/pmecc.h
+++ b/drivers/mtd/nand/raw/atmel/pmecc.h
@@ -9,10 +9,10 @@
*
* 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)
*
- * Derived from drivers/mtd/spia.c
+ * Derived from drivers/mtd/spia.c (removed in v3.8)
* Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
*
*
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c
index 8ab827edf94e..df0ef1f1e2f5 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/raw/au1550nd.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand/au1550nd.c
- *
* Copyright (C) 2004 Embedded Edge, LLC
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/mtd/nand/bcm47xxnflash/Makefile b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile
index f05b119e134b..f05b119e134b 100644
--- a/drivers/mtd/nand/bcm47xxnflash/Makefile
+++ b/drivers/mtd/nand/raw/bcm47xxnflash/Makefile
diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h
index 201b9baa52a0..201b9baa52a0 100644
--- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h
+++ b/drivers/mtd/nand/raw/bcm47xxnflash/bcm47xxnflash.h
diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/raw/bcm47xxnflash/main.c
index fb31429b70a9..fb31429b70a9 100644
--- a/drivers/mtd/nand/bcm47xxnflash/main.c
+++ b/drivers/mtd/nand/raw/bcm47xxnflash/main.c
diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c
index 54bac5b73f0a..60874de430eb 100644
--- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
+++ b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c
@@ -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_buf = bcm47xxnflash_ops_bcm4706_read_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.onfi_get_features = nand_onfi_get_set_features_notsupp;
+ b47n->nand_chip.set_features = nand_get_set_features_notsupp;
+ b47n->nand_chip.get_features = nand_get_set_features_notsupp;
nand_chip->chip_delay = 50;
b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH;
diff --git a/drivers/mtd/nand/brcmnand/Makefile b/drivers/mtd/nand/raw/brcmnand/Makefile
index 195b845e48b8..195b845e48b8 100644
--- a/drivers/mtd/nand/brcmnand/Makefile
+++ b/drivers/mtd/nand/raw/brcmnand/Makefile
diff --git a/drivers/mtd/nand/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c
index 59444b3a697d..59444b3a697d 100644
--- a/drivers/mtd/nand/brcmnand/bcm63138_nand.c
+++ b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c
diff --git a/drivers/mtd/nand/brcmnand/bcm6368_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c
index 34c91b0e1e69..34c91b0e1e69 100644
--- a/drivers/mtd/nand/brcmnand/bcm6368_nand.c
+++ b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
index c28fd2bc1a84..1306aaa7a8bf 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
@@ -2297,7 +2297,11 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
if (ret)
return ret;
- return mtd_device_register(mtd, NULL, 0);
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ nand_cleanup(chip);
+
+ return ret;
}
static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h
index 5c44cd4aba87..5c44cd4aba87 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.h
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h
diff --git a/drivers/mtd/nand/brcmnand/brcmstb_nand.c b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c
index 5c271077ac87..5c271077ac87 100644
--- a/drivers/mtd/nand/brcmnand/brcmstb_nand.c
+++ b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c
diff --git a/drivers/mtd/nand/brcmnand/iproc_nand.c b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c
index 4c6ae113664d..4c6ae113664d 100644
--- a/drivers/mtd/nand/brcmnand/iproc_nand.c
+++ b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c
index 567ff972d5fc..d8c8c9d1e640 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/raw/cafe_nand.c
@@ -645,8 +645,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
cafe->nand.read_buf = cafe_read_buf;
cafe->nand.write_buf = cafe_write_buf;
cafe->nand.select_chip = cafe_select_chip;
- cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp;
- cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp;
+ cafe->nand.set_features = nand_get_set_features_notsupp;
+ cafe->nand.get_features = nand_get_set_features_notsupp;
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_md = &cafe_bbt_mirror_descr_512;
} else {
- printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
- mtd->writesize);
+ pr_warn("Unexpected NAND flash writesize %d. Aborting\n",
+ mtd->writesize);
goto out_free_dma;
}
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);
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;
+ out_cleanup_nand:
+ nand_cleanup(&cafe->nand);
out_free_dma:
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
out_irq:
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/raw/cmx270_nand.c
index b01c9804590e..02d6751e9efe 100644
--- a/drivers/mtd/nand/cmx270_nand.c
+++ b/drivers/mtd/nand/raw/cmx270_nand.c
@@ -1,10 +1,8 @@
/*
- * linux/drivers/mtd/nand/cmx270-nand.c
- *
* Copyright (C) 2006 Compulab, Ltd.
* 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) 2001 Thomas Gleixner (gleixner@autronix.de)
*
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c
index d48877540f14..82269fde9e66 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/raw/cs553x_nand.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand/cs553x_nand.c
- *
* (C) 2005, 2006 Red Hat Inc.
*
* 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 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) {
- 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;
}
@@ -211,7 +210,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
/* map physical address */
this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
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;
goto out_mtd;
}
@@ -295,7 +294,7 @@ static int __init cs553x_init(void)
/* If it doesn't have the NAND controller enabled, abort */
rdmsrl(MSR_DIVIL_BALL_OPTS, val);
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;
}
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
index ccc8c43abcff..0f09518d980f 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/raw/davinci_nand.c
@@ -826,7 +826,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
else
ret = mtd_device_register(mtd, NULL, 0);
if (ret < 0)
- goto err;
+ goto err_cleanup_nand;
val = davinci_nand_readl(info, NRCSR_OFFSET);
dev_info(&pdev->dev, "controller rev. %d.%d\n",
@@ -834,6 +834,9 @@ static int nand_davinci_probe(struct platform_device *pdev)
return 0;
+err_cleanup_nand:
+ nand_cleanup(&info->chip);
+
err:
clk_disable_unprepare(info->clk);
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/raw/denali.c
index 313c7f50621b..2a302a1d1430 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -1384,10 +1384,12 @@ int denali_init(struct denali_nand_info *denali)
ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
- goto free_buf;
+ goto cleanup_nand;
}
return 0;
+cleanup_nand:
+ nand_cleanup(chip);
free_buf:
kfree(denali->buf);
disable_irq:
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/raw/denali.h
index 9ad33d237378..9ad33d237378 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/raw/denali.h
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c
index cfd33e6ca77f..cfd33e6ca77f 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/raw/denali_dt.c
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index 49cb3e1f8bd0..49cb3e1f8bd0 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c
index 6bc93ea66f50..86a258de0b75 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/raw/diskonchip.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand/diskonchip.c
- *
* (C) 2003 Red Hat, Inc.
* (C) 2004 Dan Brown <dan_brown@ieee.org>
* (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);
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;
}
}
@@ -438,7 +436,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd)
break;
}
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)
@@ -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);
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))
WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
else
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
if (no_ecc_failures && mtd_is_eccerr(ret)) {
- printk(KERN_ERR "suppressing ECC failure\n");
+ pr_err("suppressing ECC failure\n");
ret = 0;
}
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)
continue;
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))
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) {
doc->mh0_page = offs >> this->page_shift;
if (!findmirror)
@@ -1029,7 +1028,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch
return 2;
}
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;
}
/* 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);
if (retlen != mtd->writesize) {
/* 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 1;
@@ -1068,11 +1067,11 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
le16_to_cpus(&mh->FirstPhysicalEUN);
le32_to_cpus(&mh->FormattedSize);
- printk(KERN_INFO " DataOrgID = %s\n"
- " NumEraseUnits = %d\n"
- " FirstPhysicalEUN = %d\n"
- " FormattedSize = %d\n"
- " UnitSizeFactor = %d\n",
+ pr_info(" DataOrgID = %s\n"
+ " NumEraseUnits = %d\n"
+ " FirstPhysicalEUN = %d\n"
+ " FormattedSize = %d\n"
+ " UnitSizeFactor = %d\n",
mh->DataOrgID, mh->NumEraseUnits,
mh->FirstPhysicalEUN, mh->FormattedSize,
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);
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
@@ -1103,13 +1102,13 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
if (mh->UnitSizeFactor != 0xff) {
this->bbt_erase_shift += (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;
maxblocks = min(32768U, mtd->erasesize - psize);
}
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;
}
@@ -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->PercentUsed);
- printk(KERN_INFO " bootRecordID = %s\n"
- " NoOfBootImageBlocks = %d\n"
- " NoOfBinaryPartitions = %d\n"
- " NoOfBDTLPartitions = %d\n"
- " BlockMultiplerBits = %d\n"
- " FormatFlgs = %d\n"
- " OsakVersion = %d.%d.%d.%d\n"
- " PercentUsed = %d\n",
+ pr_info(" bootRecordID = %s\n"
+ " NoOfBootImageBlocks = %d\n"
+ " NoOfBinaryPartitions = %d\n"
+ " NoOfBDTLPartitions = %d\n"
+ " BlockMultiplerBits = %d\n"
+ " FormatFlgs = %d\n"
+ " OsakVersion = %d.%d.%d.%d\n"
+ " PercentUsed = %d\n",
mh->bootRecordID, mh->NoOfBootImageBlocks,
mh->NoOfBinaryPartitions,
mh->NoOfBDTLPartitions,
@@ -1202,13 +1201,13 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti
blocks = mtd->size >> vshift;
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;
}
blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
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;
}
@@ -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->Reserved0);
- printk(KERN_INFO " PARTITION[%d] ->\n"
+ pr_info(" PARTITION[%d] ->\n"
" virtualUnits = %d\n"
" firstUnit = %d\n"
" lastUnit = %d\n"
@@ -1308,7 +1307,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
struct mtd_partition parts[5];
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;
}
@@ -1436,7 +1435,8 @@ static int __init doc_probe(unsigned long physadr)
return -EBUSY;
virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
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;
goto error_ioremap;
}
@@ -1495,7 +1495,7 @@ static int __init doc_probe(unsigned long physadr)
reg = DoC_Mplus_Toggle;
break;
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:
ret = -ENODEV;
goto notfound;
@@ -1511,7 +1511,7 @@ static int __init doc_probe(unsigned long physadr)
tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
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;
goto notfound;
}
@@ -1545,12 +1545,13 @@ static int __init doc_probe(unsigned long physadr)
}
newval = ~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;
}
}
- 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) +
(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);
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;
}
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);
if (ret < 0)
goto outerr;
@@ -1682,7 +1684,7 @@ static int __init init_nanddoc(void)
/* No banner message any more. Print a message if no DiskOnChip
found, so the user knows we at least tried. */
if (!doclist) {
- printk(KERN_INFO "No valid DiskOnChip devices found\n");
+ pr_info("No valid DiskOnChip devices found\n");
ret = -ENODEV;
goto outerr;
}
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/raw/docg4.c
index 72f1327c4430..1314aa99b9ab 100644
--- a/drivers/mtd/nand/docg4.c
+++ b/drivers/mtd/nand/raw/docg4.c
@@ -1269,8 +1269,8 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
nand->read_buf = docg4_read_buf;
nand->write_buf = docg4_write_buf16;
nand->erase = docg4_erase_block;
- nand->onfi_set_features = nand_onfi_get_set_features_notsupp;
- nand->onfi_get_features = nand_onfi_get_set_features_notsupp;
+ nand->set_features = nand_get_set_features_notsupp;
+ nand->get_features = nand_get_set_features_notsupp;
nand->ecc.read_page = docg4_read_page;
nand->ecc.write_page = docg4_write_page;
nand->ecc.read_page_raw = docg4_read_page_raw;
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c
index 8b6dcd739ecb..d28df991c73c 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c
@@ -775,8 +775,8 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
chip->select_chip = fsl_elbc_select_chip;
chip->cmdfunc = fsl_elbc_cmdfunc;
chip->waitfunc = fsl_elbc_wait;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
chip->bbt_td = &bbt_main_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,
NULL, 0);
- printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n",
- (unsigned long long)res.start, priv->bank);
+ pr_info("eLBC NAND device at 0x%llx, bank %d\n",
+ (unsigned long long)res.start, priv->bank);
return 0;
err:
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 5a9c2f0020c2..61aae0224078 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -799,7 +799,7 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
msecs_to_jiffies(IFC_TIMEOUT_MSECS));
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 */
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->cmdfunc = fsl_ifc_cmdfunc;
chip->waitfunc = fsl_ifc_wait;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c
index a88e2cf66e0f..a88e2cf66e0f 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/raw/fsl_upm.c
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
index f49ed46fa770..28c48dcc514e 100644
--- a/drivers/mtd/nand/fsmc_nand.c
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand/fsmc_nand.c
- *
* ST Microelectronics
* Flexible Static Memory Controller (FSMC)
* Driver for NAND portions
@@ -9,7 +7,9 @@
* Vipin Kumar <vipin.kumar@st.com>
* 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
* License version 2. This program is licensed "as is" without any
@@ -103,10 +103,6 @@
#define ECC3 0x1C
#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)
struct fsmc_nand_timings {
@@ -143,7 +139,7 @@ enum access_mode {
* @data_va: NAND port for Data.
* @cmd_va: NAND port for Command.
* @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 {
u32 pid;
@@ -258,45 +254,6 @@ static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd)
}
/*
- * 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
*
* This routine initializes timing parameters related to NAND memory access in
@@ -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 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;
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;
if (host->nand.options & NAND_BUSWIDTH_16)
- writel_relaxed(value | FSMC_DEVWID_16,
- FSMC_NAND_REG(regs, bank, PC));
+ writel_relaxed(value | FSMC_DEVWID_16, host->regs_va + PC);
else
- writel_relaxed(value | FSMC_DEVWID_8,
- FSMC_NAND_REG(regs, bank, PC));
-
- writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar,
- FSMC_NAND_REG(regs, bank, PC));
- writel_relaxed(thiz | thold | twait | tset,
- FSMC_NAND_REG(regs, bank, COMM));
- writel_relaxed(thiz | thold | twait | tset,
- FSMC_NAND_REG(regs, bank, ATTRIB));
+ writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + PC);
+
+ writel_relaxed(readl(host->regs_va + PC) | tclr | tar,
+ host->regs_va + PC);
+ writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM);
+ writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB);
}
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)
{
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,
- FSMC_NAND_REG(regs, bank, PC));
- writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN,
- FSMC_NAND_REG(regs, bank, PC));
- writel_relaxed(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN,
- FSMC_NAND_REG(regs, bank, PC));
+
+ writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCPLEN_256,
+ host->regs_va + PC);
+ writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCEN,
+ host->regs_va + PC);
+ writel_relaxed(readl(host->regs_va + PC) | FSMC_ECCEN,
+ host->regs_va + PC);
}
/*
@@ -439,13 +388,11 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
uint8_t *ecc)
{
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
- void __iomem *regs = host->regs_va;
- uint32_t bank = host->bank;
uint32_t ecc_tmp;
unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
do {
- if (readl_relaxed(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY)
+ if (readl_relaxed(host->regs_va + STS) & FSMC_CODE_RDY)
break;
else
cond_resched();
@@ -456,25 +403,25 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
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[1] = (uint8_t) (ecc_tmp >> 8);
ecc[2] = (uint8_t) (ecc_tmp >> 16);
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[5] = (uint8_t) (ecc_tmp >> 8);
ecc[6] = (uint8_t) (ecc_tmp >> 16);
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[9] = (uint8_t) (ecc_tmp >> 8);
ecc[10] = (uint8_t) (ecc_tmp >> 16);
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);
return 0;
@@ -489,11 +436,9 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
uint8_t *ecc)
{
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
- void __iomem *regs = host->regs_va;
- uint32_t bank = host->bank;
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[1] = (uint8_t) (ecc_tmp >> 8);
ecc[2] = (uint8_t) (ecc_tmp >> 16);
@@ -598,18 +543,18 @@ unmap_dma:
*/
static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
IS_ALIGNED(len, sizeof(uint32_t))) {
uint32_t *p = (uint32_t *)buf;
len = len >> 2;
for (i = 0; i < len; i++)
- writel_relaxed(p[i], chip->IO_ADDR_W);
+ writel_relaxed(p[i], host->data_va);
} else {
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)
{
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
int i;
- struct nand_chip *chip = mtd_to_nand(mtd);
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
IS_ALIGNED(len, sizeof(uint32_t))) {
uint32_t *p = (uint32_t *)buf;
len = len >> 2;
for (i = 0; i < len; i++)
- p[i] = readl_relaxed(chip->IO_ADDR_R);
+ p[i] = readl_relaxed(host->data_va);
} else {
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);
}
+/* 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
* @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 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 num_err, i;
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 */
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
* array
*/
- ecc1 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC1));
- ecc2 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC2));
- ecc3 = readl_relaxed(FSMC_NAND_REG(regs, bank, ECC3));
- ecc4 = readl_relaxed(FSMC_NAND_REG(regs, bank, STS));
+ ecc1 = readl_relaxed(host->regs_va + ECC1);
+ ecc2 = readl_relaxed(host->regs_va + ECC2);
+ ecc3 = readl_relaxed(host->regs_va + ECC3);
+ ecc4 = readl_relaxed(host->regs_va + STS);
err_idx[0] = (ecc1 >> 0) & 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 nand_chip *nand;
struct resource *res;
+ void __iomem *base;
dma_cap_mask_t mask;
int ret = 0;
u32 pid;
@@ -923,9 +963,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
return PTR_ERR(host->cmd_va);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
- host->regs_va = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(host->regs_va))
- return PTR_ERR(host->regs_va);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ 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);
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.
*/
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;
dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, "
"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);
mtd->dev.parent = &pdev->dev;
- nand->IO_ADDR_R = host->data_va;
- nand->IO_ADDR_W = host->data_va;
- nand->cmd_ctrl = fsmc_cmd_ctrl;
+ nand->exec_op = fsmc_exec_op;
+ nand->select_chip = fsmc_select_chip;
nand->chip_delay = 30;
/*
@@ -974,8 +1016,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
nand->ecc.size = 512;
nand->badblockbits = 7;
- switch (host->mode) {
- case USE_DMA_ACCESS:
+ if (host->mode == USE_DMA_ACCESS) {
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
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");
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)
diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/raw/gpio.c
index a8bde6665c24..2780af26d9ab 100644
--- a/drivers/mtd/nand/gpio.c
+++ b/drivers/mtd/nand/raw/gpio.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand/gpio.c
- *
* Updated, and converted to generic GPIO based driver by Russell King.
*
* Written by Ben Dooks <ben@simtec.co.uk>
diff --git a/drivers/mtd/nand/gpmi-nand/Makefile b/drivers/mtd/nand/raw/gpmi-nand/Makefile
index 3a462487c35e..3a462487c35e 100644
--- a/drivers/mtd/nand/gpmi-nand/Makefile
+++ b/drivers/mtd/nand/raw/gpmi-nand/Makefile
diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h
index 05bb91f2f4c4..05bb91f2f4c4 100644
--- a/drivers/mtd/nand/gpmi-nand/bch-regs.h
+++ b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
index 97787246af41..e94556705dc7 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
@@ -26,15 +26,8 @@
#include "gpmi-regs.h"
#include "bch-regs.h"
-static struct timing_threshold timing_default_threshold = {
- .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
- 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,
-};
+/* Converts time to clock cycles */
+#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period)
#define MXS_SET_ADDR 0x4
#define MXS_CLR_ADDR 0x8
@@ -151,8 +144,15 @@ err_clk:
return ret;
}
-#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true)
-#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false)
+int gpmi_enable_clk(struct gpmi_nand_data *this)
+{
+ 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)
{
@@ -174,7 +174,6 @@ int gpmi_init(struct gpmi_nand_data *this)
if (ret)
goto err_out;
-
/* Choose NAND mode. */
writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR);
@@ -313,467 +312,6 @@ err_out:
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.
* The GPMI-clock is the internal clock in the gpmi nand controller.
@@ -824,13 +362,10 @@ return_results:
* 4.1) From the aspect of the nand chip pins:
* Delay = (tREA + C - tRP) {1}
*
- * tREA : the maximum read access time. From the ONFI nand standards,
- * we know that tREA is 16ns in mode 5, tREA is 20ns is mode 4.
- * Please check it in : www.onfi.org
- * C : a constant for adjust the delay. default is 4.
- * tRP : the read pulse width.
- * Specified by the HW_GPMI_TIMING0:DATA_SETUP:
- * tRP = (GPMI-clock-period) * DATA_SETUP
+ * tREA : the maximum read access time.
+ * C : a constant to adjust the delay. default is 4000ps.
+ * tRP : the read pulse width, which is exactly:
+ * tRP = (GPMI-clock-period) * DATA_SETUP
*
* 4.2) From the aspect of the GPMI nand controller:
* Delay = RDN_DELAY * 0.125 * RP {2}
@@ -843,239 +378,137 @@ return_results:
*
* Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period
* 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:
*
- * (tREA + 4 - tRP) * 8
- * RDN_DELAY = --------------------- {3}
+ * (tREA + 4000 - tRP) * 8
+ * RDN_DELAY = ----------------------- {3}
* 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,
- struct gpmi_nfc_hardware_timing *hw)
+static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
+ const struct nand_sdr_timings *sdr)
{
- struct resources *r = &this->resources;
- unsigned long rate = clk_get_rate(r->clock[0]);
- int mode = this->timing_mode;
- int dll_threshold = this->devdata->max_chain_delay;
- unsigned long delay;
- unsigned long clk_period;
- int t_rea;
- int c = 4;
- int t_rp;
- int rp;
+ struct gpmi_nfc_hardware_timing *hw = &this->hw;
+ unsigned int dll_threshold_ps = this->devdata->max_chain_delay;
+ unsigned int period_ps, reference_period_ps;
+ unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles;
+ unsigned int tRP_ps;
+ bool use_half_period;
+ int sample_delay_ps, sample_delay_factor;
+ u16 busy_timeout_cycles;
+ u8 wrn_dly_sel;
+
+ if (sdr->tRC_min >= 30000) {
+ /* ONFI non-EDO modes [0-3] */
+ hw->clk_rate = 22000000;
+ wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
+ } else if (sdr->tRC_min >= 25000) {
+ /* ONFI EDO mode 4 */
+ hw->clk_rate = 80000000;
+ wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
+ } else {
+ /* ONFI EDO mode 5 */
+ hw->clk_rate = 100000000;
+ wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
+ }
- /*
- * [1] for GPMI_HW_GPMI_TIMING0:
- * The async mode requires 40MHz for mode 4, 50MHz for mode 5.
- * The GPMI can support 100MHz at most. So if we want to
- * get the 40MHz or 50MHz, we have to set DS=1, DH=1.
- * Set the ADDRESS_SETUP to 0 in mode 4.
- */
- hw->data_setup_in_cycles = 1;
- hw->data_hold_in_cycles = 1;
- hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0);
+ /* SDR core timings are given in picoseconds */
+ period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate);
- /* [2] for GPMI_HW_GPMI_TIMING1 */
- hw->device_busy_timeout = 0x9000;
+ 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);
- /* [3] for GPMI_HW_GPMI_CTRL1 */
- hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
+ 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);
/*
- * Enlarge 10 times for the numerator and denominator in {3}.
- * This make us to get more accurate result.
+ * Derive NFC ideal delay from {3}:
+ *
+ * (tREA + 4000 - tRP) * 8
+ * RDN_DELAY = -----------------------
+ * RP
*/
- 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;
+ if (period_ps > dll_threshold_ps) {
+ use_half_period = true;
+ reference_period_ps = period_ps / 2;
} else {
- hw->use_half_periods = 0;
- rp = clk_period;
+ use_half_period = false;
+ reference_period_ps = period_ps;
}
- /*
- * Multiply the numerator with 10, we could do a round off:
- * 7.8 round up to 8; 7.4 round down to 7.
- */
- delay = (((t_rea + c - t_rp) * 8) * 10) / rp;
- delay = (delay + 5) / 10;
-
- hw->sample_delay_factor = delay;
-}
-
-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;
+ 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;
- return enable_edo_mode(this, mode);
- }
- return 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_begin(struct gpmi_nand_data *this)
+void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
{
+ struct gpmi_nfc_hardware_timing *hw = &this->hw;
struct resources *r = &this->resources;
void __iomem *gpmi_regs = r->gpmi_regs;
- unsigned int clock_period_in_ns;
- uint32_t reg;
- unsigned int dll_wait_time_in_us;
- struct gpmi_nfc_hardware_timing hw;
- int ret;
-
- /* Enable the clock. */
- 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 */
- if (this->flags & GPMI_TIMING_INIT_OK)
- return;
- this->flags |= GPMI_TIMING_INIT_OK;
+ unsigned int dll_wait_time_us;
- 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);
+ clk_set_rate(r->clock[0], hw->clk_rate);
- /* [3] The following code is to set the HW_GPMI_CTRL1. */
+ writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0);
+ writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1);
- /* 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);
+ /*
+ * Clear several CTRL1 fields, DLL must be disabled when setting
+ * RDN_DELAY or HALF_PERIOD.
+ */
+ writel(BM_GPMI_CTRL1_CLEAR_MASK, gpmi_regs + HW_GPMI_CTRL1_CLR);
+ writel(hw->ctrl1n, gpmi_regs + HW_GPMI_CTRL1_SET);
- /* If no sample delay is called for, return immediately. */
- if (!hw.sample_delay_factor)
- return;
+ /* Wait 64 clock cycles before using the GPMI after enabling the DLL */
+ dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64;
+ if (!dll_wait_time_us)
+ dll_wait_time_us = 1;
- /* 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);
+ /* Wait for the DLL to settle. */
+ udelay(dll_wait_time_us);
+}
- writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET);
+int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr,
+ const struct nand_data_interface *conf)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
+ const struct nand_sdr_timings *sdr;
- /* At last, we enable the DLL. */
- writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET);
+ /* Retrieve required NAND timings */
+ sdr = nand_get_sdr_timings(conf);
+ if (IS_ERR(sdr))
+ return PTR_ERR(sdr);
- /*
- * After we enable the GPMI DLL, we have to wait 64 clock cycles before
- * we can use the GPMI. Calculate the amount of time we need to wait,
- * in microseconds.
- */
- clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]);
- dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000;
+ /* Only MX6 GPMI controller can reach EDO timings */
+ if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this))
+ return -ENOTSUPP;
- if (!dll_wait_time_in_us)
- dll_wait_time_in_us = 1;
+ /* Stop here if this call was just a check */
+ if (chipnr < 0)
+ return 0;
- /* Wait for the DLL to settle. */
- udelay(dll_wait_time_in_us);
+ /* Do the actual derivation of the controller timings */
+ gpmi_nfc_compute_timings(this, sdr);
-err_out:
- return;
-}
+ this->hw.must_apply_timings = true;
-void gpmi_end(struct gpmi_nand_data *this)
-{
- gpmi_disable_clk(this);
+ return 0;
}
/* Clears a BCH interrupt. */
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
index 61fdd733492f..c2597c8107a0 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
@@ -94,7 +94,7 @@ static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = {
static const struct gpmi_devdata gpmi_devdata_imx23 = {
.type = IS_MX23,
.bch_max_ecc_strength = 20,
- .max_chain_delay = 16,
+ .max_chain_delay = 16000,
.clks = 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 = {
.type = IS_MX28,
.bch_max_ecc_strength = 20,
- .max_chain_delay = 16,
+ .max_chain_delay = 16000,
.clks = 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 = {
.type = IS_MX6Q,
.bch_max_ecc_strength = 40,
- .max_chain_delay = 12,
+ .max_chain_delay = 12000,
.clks = 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 = {
.type = IS_MX6SX,
.bch_max_ecc_strength = 62,
- .max_chain_delay = 12,
+ .max_chain_delay = 12000,
.clks = 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 = {
.type = IS_MX7D,
.bch_max_ecc_strength = 62,
- .max_chain_delay = 12,
+ .max_chain_delay = 12000,
.clks = 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);
}
-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,
void *destination, unsigned length,
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 gpmi_nand_data *this = nand_get_controller_data(chip);
+ int ret;
- if ((this->current_chip < 0) && (chipnr >= 0))
- gpmi_begin(this);
- else if ((this->current_chip >= 0) && (chipnr < 0))
- gpmi_end(this);
+ /*
+ * For power consumption matters, disable/enable the clock each time a
+ * die is selected/unselected.
+ */
+ 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;
}
@@ -1955,14 +1948,6 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
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;
}
@@ -1983,6 +1968,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
nand_set_controller_data(chip, this);
nand_set_flash_node(chip, this->pdev->dev.of_node);
chip->select_chip = gpmi_select_chip;
+ chip->setup_data_interface = gpmi_setup_data_interface;
chip->cmd_ctrl = gpmi_cmd_ctrl;
chip->dev_ready = gpmi_dev_ready;
chip->read_byte = gpmi_read_byte;
@@ -2093,7 +2079,7 @@ static int gpmi_nand_probe(struct platform_device *pdev)
if (ret)
goto exit_acquire_resources;
- ret = init_hardware(this);
+ ret = gpmi_init(this);
if (ret)
goto exit_nfc_init;
@@ -2141,7 +2127,6 @@ static int gpmi_pm_resume(struct device *dev)
return ret;
/* re-init the GPMI registers */
- this->flags &= ~GPMI_TIMING_INIT_OK;
ret = gpmi_init(this);
if (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;
}
- /* re-init others */
- gpmi_extra_init(this);
-
return 0;
}
#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h
index 06c1f993912c..62fde59b995f 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h
@@ -86,39 +86,6 @@ enum dma_ops_type {
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 {
IS_MX23,
IS_MX28,
@@ -135,11 +102,27 @@ struct gpmi_devdata {
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 {
- /* flags */
-#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
-#define GPMI_TIMING_INIT_OK (1 << 1)
- int flags;
+ /* Devdata */
const struct gpmi_devdata *devdata;
/* System Interface */
@@ -150,8 +133,7 @@ struct gpmi_nand_data {
struct resources resources;
/* Flash Hardware */
- struct nand_timing timing;
- int timing_mode;
+ struct gpmi_nfc_hardware_timing hw;
/* BCH */
struct bch_geometry bch_geometry;
@@ -204,69 +186,6 @@ struct gpmi_nand_data {
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 */
int common_nfc_set_geometry(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 */
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_dump_info(struct gpmi_nand_data *);
int bch_set_geometry(struct gpmi_nand_data *);
int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
int gpmi_send_command(struct gpmi_nand_data *);
-void gpmi_begin(struct gpmi_nand_data *);
-void gpmi_end(struct gpmi_nand_data *);
+int gpmi_enable_clk(struct gpmi_nand_data *this);
+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_send_data(struct gpmi_nand_data *);
int gpmi_send_page(struct gpmi_nand_data *,
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h
index 82114cdc8330..d92bf32221ca 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h
@@ -147,6 +147,11 @@
#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 BP_GPMI_TIMING0_ADDRESS_SETUP 16
diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c
index cb862793ab6d..27558a67fa41 100644
--- a/drivers/mtd/nand/hisi504_nand.c
+++ b/drivers/mtd/nand/raw/hisi504_nand.c
@@ -762,8 +762,8 @@ static int hisi_nfc_probe(struct platform_device *pdev)
chip->write_buf = hisi_nfc_write_buf;
chip->read_buf = hisi_nfc_read_buf;
chip->chip_delay = HINFC504_CHIP_DELAY;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
hisi_nfc_host_init(host);
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c
index 613b00a9604b..613b00a9604b 100644
--- a/drivers/mtd/nand/jz4740_nand.c
+++ b/drivers/mtd/nand/raw/jz4740_nand.c
diff --git a/drivers/mtd/nand/jz4780_bch.c b/drivers/mtd/nand/raw/jz4780_bch.c
index 731c6051d91e..731c6051d91e 100644
--- a/drivers/mtd/nand/jz4780_bch.c
+++ b/drivers/mtd/nand/raw/jz4780_bch.c
diff --git a/drivers/mtd/nand/jz4780_bch.h b/drivers/mtd/nand/raw/jz4780_bch.h
index bf4718088a3a..bf4718088a3a 100644
--- a/drivers/mtd/nand/jz4780_bch.h
+++ b/drivers/mtd/nand/raw/jz4780_bch.h
diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/raw/jz4780_nand.c
index e69f6ae4c539..e69f6ae4c539 100644
--- a/drivers/mtd/nand/jz4780_nand.c
+++ b/drivers/mtd/nand/raw/jz4780_nand.c
diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c
index e357948a7505..e357948a7505 100644
--- a/drivers/mtd/nand/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c
diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
index 5f7cc6da0a7f..5f7cc6da0a7f 100644
--- a/drivers/mtd/nand/lpc32xx_slc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
diff --git a/drivers/mtd/nand/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c
index 03805f9669da..10e953218948 100644
--- a/drivers/mtd/nand/marvell_nand.c
+++ b/drivers/mtd/nand/raw/marvell_nand.c
@@ -307,7 +307,8 @@ struct marvell_nfc_caps {
* @controller: Base controller structure
* @dev: Parent device (used to print error messages)
* @regs: NAND controller registers
- * @ecc_clk: ECC block clock, two times the NAND controller clock
+ * @core_clk: Core clock
+ * @reg_clk: Regiters clock
* @complete: Completion object to wait for NAND controller events
* @assigned_cs: Bitmask describing already assigned CS lines
* @chips: List containing all the NAND chips attached to
@@ -320,7 +321,8 @@ struct marvell_nfc {
struct nand_hw_control controller;
struct device *dev;
void __iomem *regs;
- struct clk *ecc_clk;
+ struct clk *core_clk;
+ struct clk *reg_clk;
struct completion complete;
unsigned long assigned_cs;
struct list_head chips;
@@ -379,6 +381,8 @@ struct marvell_nfc_timings {
* return the number of clock periods.
*/
#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns))
+#define TO_CYCLES64(ps, period_ns) (DIV_ROUND_UP_ULL(div_u64(ps, 1000), \
+ period_ns))
/**
* NAND driver structure filled during the parsing of the ->exec_op() subop
@@ -2189,7 +2193,7 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr,
struct nand_chip *chip = mtd_to_nand(mtd);
struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
- unsigned int period_ns = 1000000000 / clk_get_rate(nfc->ecc_clk) * 2;
+ unsigned int period_ns = 1000000000 / clk_get_rate(nfc->core_clk) * 2;
const struct nand_sdr_timings *sdr;
struct marvell_nfc_timings nfc_tmg;
int read_delay;
@@ -2236,8 +2240,20 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr,
nfc_tmg.tRHW = TO_CYCLES(max_t(int, sdr->tRHW_min, sdr->tCCS_min),
period_ns);
- /* Use WAIT_MODE (wait for RB line) instead of only relying on delays */
- nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns);
+ /*
+ * NFCv2: Use WAIT_MODE (wait for RB line), do not rely only on delays.
+ * NFCv1: No WAIT_MODE, tR must be maximal.
+ */
+ if (nfc->caps->is_nfcv2) {
+ nfc_tmg.tR = TO_CYCLES(sdr->tWB_max, period_ns);
+ } else {
+ nfc_tmg.tR = TO_CYCLES64(sdr->tWB_max + sdr->tR_max,
+ period_ns);
+ if (nfc_tmg.tR + 3 > nfc_tmg.tCH)
+ nfc_tmg.tR = nfc_tmg.tCH - 3;
+ else
+ nfc_tmg.tR = 0;
+ }
if (chipnr < 0)
return 0;
@@ -2249,18 +2265,24 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr,
NDTR0_TWP(nfc_tmg.tWP) |
NDTR0_TWH(nfc_tmg.tWH) |
NDTR0_TCS(nfc_tmg.tCS) |
- NDTR0_TCH(nfc_tmg.tCH) |
- NDTR0_RD_CNT_DEL(read_delay) |
- NDTR0_SELCNTR |
- NDTR0_TADL(nfc_tmg.tADL);
+ NDTR0_TCH(nfc_tmg.tCH);
marvell_nand->ndtr1 =
NDTR1_TAR(nfc_tmg.tAR) |
NDTR1_TWHR(nfc_tmg.tWHR) |
- NDTR1_TRHW(nfc_tmg.tRHW) |
- NDTR1_WAIT_MODE |
NDTR1_TR(nfc_tmg.tR);
+ if (nfc->caps->is_nfcv2) {
+ marvell_nand->ndtr0 |=
+ NDTR0_RD_CNT_DEL(read_delay) |
+ NDTR0_SELCNTR |
+ NDTR0_TADL(nfc_tmg.tADL);
+
+ marvell_nand->ndtr1 |=
+ NDTR1_TRHW(nfc_tmg.tRHW) |
+ NDTR1_WAIT_MODE;
+ }
+
return 0;
}
@@ -2395,8 +2417,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
chip->exec_op = marvell_nfc_exec_op;
chip->select_chip = marvell_nfc_select_chip;
- if (nfc->caps->is_nfcv2 &&
- !of_property_read_bool(np, "marvell,nand-keep-config"))
+ if (!of_property_read_bool(np, "marvell,nand-keep-config"))
chip->setup_data_interface = marvell_nfc_setup_data_interface;
mtd = nand_to_mtd(chip);
@@ -2738,20 +2759,37 @@ static int marvell_nfc_probe(struct platform_device *pdev)
return irq;
}
- nfc->ecc_clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(nfc->ecc_clk))
- return PTR_ERR(nfc->ecc_clk);
+ nfc->core_clk = devm_clk_get(&pdev->dev, "core");
- ret = clk_prepare_enable(nfc->ecc_clk);
+ /* Managed the legacy case (when the first clock was not named) */
+ if (nfc->core_clk == ERR_PTR(-ENOENT))
+ nfc->core_clk = devm_clk_get(&pdev->dev, NULL);
+
+ if (IS_ERR(nfc->core_clk))
+ return PTR_ERR(nfc->core_clk);
+
+ ret = clk_prepare_enable(nfc->core_clk);
if (ret)
return ret;
+ nfc->reg_clk = devm_clk_get(&pdev->dev, "reg");
+ if (PTR_ERR(nfc->reg_clk) != -ENOENT) {
+ if (!IS_ERR(nfc->reg_clk)) {
+ ret = clk_prepare_enable(nfc->reg_clk);
+ if (ret)
+ goto unprepare_core_clk;
+ } else {
+ ret = PTR_ERR(nfc->reg_clk);
+ goto unprepare_core_clk;
+ }
+ }
+
marvell_nfc_disable_int(nfc, NDCR_ALL_INT);
marvell_nfc_clear_int(nfc, NDCR_ALL_INT);
ret = devm_request_irq(dev, irq, marvell_nfc_isr,
0, "marvell-nfc", nfc);
if (ret)
- goto unprepare_clk;
+ goto unprepare_reg_clk;
/* Get NAND controller capabilities */
if (pdev->id_entry)
@@ -2762,24 +2800,26 @@ static int marvell_nfc_probe(struct platform_device *pdev)
if (!nfc->caps) {
dev_err(dev, "Could not retrieve NFC caps\n");
ret = -EINVAL;
- goto unprepare_clk;
+ goto unprepare_reg_clk;
}
/* Init the controller and then probe the chips */
ret = marvell_nfc_init(nfc);
if (ret)
- goto unprepare_clk;
+ goto unprepare_reg_clk;
platform_set_drvdata(pdev, nfc);
ret = marvell_nand_chips_init(dev, nfc);
if (ret)
- goto unprepare_clk;
+ goto unprepare_reg_clk;
return 0;
-unprepare_clk:
- clk_disable_unprepare(nfc->ecc_clk);
+unprepare_reg_clk:
+ clk_disable_unprepare(nfc->reg_clk);
+unprepare_core_clk:
+ clk_disable_unprepare(nfc->core_clk);
return ret;
}
@@ -2795,7 +2835,8 @@ static int marvell_nfc_remove(struct platform_device *pdev)
dma_release_channel(nfc->dma_chan);
}
- clk_disable_unprepare(nfc->ecc_clk);
+ clk_disable_unprepare(nfc->reg_clk);
+ clk_disable_unprepare(nfc->core_clk);
return 0;
}
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c
index b6b97cc9fba6..6d1740d54e0d 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/raw/mpc5121_nfc.c
@@ -6,9 +6,8 @@
* by OSADL membership fees in 2009; for details see www.osadl.org.
*
* Based on original driver from Freescale Semiconductor
- * written by John Rigby <jrigby@freescale.com> on basis
- * of drivers/mtd/nand/mxc_nand.c. Reworked and extended
- * Piotr Ziecik <kosmo@semihalf.com>.
+ * written by John Rigby <jrigby@freescale.com> on basis of mxc_nand.c.
+ * Reworked and extended by Piotr Ziecik <kosmo@semihalf.com>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -708,8 +707,8 @@ static int mpc5121_nfc_probe(struct platform_device *op)
chip->read_buf = mpc5121_nfc_read_buf;
chip->write_buf = mpc5121_nfc_write_buf;
chip->select_chip = mpc5121_nfc_select_chip;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
chip->bbt_options = NAND_BBT_USE_FLASH;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c
index 40d86a861a70..40d86a861a70 100644
--- a/drivers/mtd/nand/mtk_ecc.c
+++ b/drivers/mtd/nand/raw/mtk_ecc.c
diff --git a/drivers/mtd/nand/mtk_ecc.h b/drivers/mtd/nand/raw/mtk_ecc.h
index a455df080952..a455df080952 100644
--- a/drivers/mtd/nand/mtk_ecc.h
+++ b/drivers/mtd/nand/raw/mtk_ecc.h
diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c
index 6977da3a26aa..6977da3a26aa 100644
--- a/drivers/mtd/nand/mtk_nand.c
+++ b/drivers/mtd/nand/raw/mtk_nand.c
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c
index f3be0b2a8869..45786e707b7b 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/raw/mxc_nand.c
@@ -140,6 +140,8 @@ struct mxc_nand_host;
struct mxc_nand_devtype_data {
void (*preset)(struct mtd_info *);
+ int (*read_page)(struct nand_chip *chip, void *buf, void *oob, bool ecc,
+ int page);
void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
void (*send_page)(struct mtd_info *, unsigned int);
@@ -150,10 +152,9 @@ struct mxc_nand_devtype_data {
u32 (*get_ecc_status)(struct mxc_nand_host *);
const struct mtd_ooblayout_ops *ooblayout;
void (*select_chip)(struct mtd_info *mtd, int chip);
- int (*correct_data)(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc);
int (*setup_data_interface)(struct mtd_info *mtd, int csline,
const struct nand_data_interface *conf);
+ void (*enable_hwecc)(struct nand_chip *chip, bool enable);
/*
* On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
@@ -252,6 +253,109 @@ static void memcpy16_toio(void __iomem *trg, const void *src, int size)
__raw_writew(*s++, t++);
}
+/*
+ * The controller splits a page into data chunks of 512 bytes + partial oob.
+ * There are writesize / 512 such chunks, the size of the partial oob parts is
+ * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then
+ * contains additionally the byte lost by rounding (if any).
+ * This function handles the needed shuffling between host->data_buf (which
+ * holds a page in natural order, i.e. writesize bytes data + oobsize bytes
+ * spare) and the NFC buffer.
+ */
+static void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(this);
+ u16 i, oob_chunk_size;
+ u16 num_chunks = mtd->writesize / 512;
+
+ u8 *d = buf;
+ u8 __iomem *s = host->spare0;
+ u16 sparebuf_size = host->devtype_data->spare_len;
+
+ /* size of oob chunk for all but possibly the last one */
+ oob_chunk_size = (host->used_oobsize / num_chunks) & ~1;
+
+ if (bfrom) {
+ for (i = 0; i < num_chunks - 1; i++)
+ memcpy16_fromio(d + i * oob_chunk_size,
+ s + i * sparebuf_size,
+ oob_chunk_size);
+
+ /* the last chunk */
+ memcpy16_fromio(d + i * oob_chunk_size,
+ s + i * sparebuf_size,
+ host->used_oobsize - i * oob_chunk_size);
+ } else {
+ for (i = 0; i < num_chunks - 1; i++)
+ memcpy16_toio(&s[i * sparebuf_size],
+ &d[i * oob_chunk_size],
+ oob_chunk_size);
+
+ /* the last chunk */
+ memcpy16_toio(&s[i * sparebuf_size],
+ &d[i * oob_chunk_size],
+ host->used_oobsize - i * oob_chunk_size);
+ }
+}
+
+/*
+ * MXC NANDFC can only perform full page+spare or spare-only read/write. When
+ * the upper layers perform a read/write buf operation, the saved column address
+ * is used to index into the full page. So usually this function is called with
+ * column == 0 (unless no column cycle is needed indicated by column == -1)
+ */
+static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ /* Write out column address, if necessary */
+ if (column != -1) {
+ host->devtype_data->send_addr(host, column & 0xff,
+ page_addr == -1);
+ if (mtd->writesize > 512)
+ /* another col addr cycle for 2k page */
+ host->devtype_data->send_addr(host,
+ (column >> 8) & 0xff,
+ false);
+ }
+
+ /* Write out page address, if necessary */
+ if (page_addr != -1) {
+ /* paddr_0 - p_addr_7 */
+ host->devtype_data->send_addr(host, (page_addr & 0xff), false);
+
+ if (mtd->writesize > 512) {
+ if (mtd->size >= 0x10000000) {
+ /* paddr_8 - paddr_15 */
+ host->devtype_data->send_addr(host,
+ (page_addr >> 8) & 0xff,
+ false);
+ host->devtype_data->send_addr(host,
+ (page_addr >> 16) & 0xff,
+ true);
+ } else
+ /* paddr_8 - paddr_15 */
+ host->devtype_data->send_addr(host,
+ (page_addr >> 8) & 0xff, true);
+ } else {
+ if (nand_chip->options & NAND_ROW_ADDR_3) {
+ /* paddr_8 - paddr_15 */
+ host->devtype_data->send_addr(host,
+ (page_addr >> 8) & 0xff,
+ false);
+ host->devtype_data->send_addr(host,
+ (page_addr >> 16) & 0xff,
+ true);
+ } else
+ /* paddr_8 - paddr_15 */
+ host->devtype_data->send_addr(host,
+ (page_addr >> 8) & 0xff, true);
+ }
+ }
+}
+
static int check_int_v3(struct mxc_nand_host *host)
{
uint32_t tmp;
@@ -575,6 +679,42 @@ static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host)
return ret;
}
+static void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ uint16_t config1;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return;
+
+ config1 = readw(NFC_V1_V2_CONFIG1);
+
+ if (enable)
+ config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
+ else
+ config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN;
+
+ writew(config1, NFC_V1_V2_CONFIG1);
+}
+
+static void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ uint32_t config2;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return;
+
+ config2 = readl(NFC_V3_CONFIG2);
+
+ if (enable)
+ config2 |= NFC_V3_CONFIG2_ECC_EN;
+ else
+ config2 &= ~NFC_V3_CONFIG2_ECC_EN;
+
+ writel(config2, NFC_V3_CONFIG2);
+}
+
/* This functions is used by upper layer to checks if device is ready */
static int mxc_nand_dev_ready(struct mtd_info *mtd)
{
@@ -585,45 +725,90 @@ static int mxc_nand_dev_ready(struct mtd_info *mtd)
return 1;
}
-static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+static int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob,
+ bool ecc, int page)
{
- /*
- * If HW ECC is enabled, we turn it on during init. There is
- * no need to enable again here.
- */
-}
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ unsigned int bitflips_corrected = 0;
+ int no_subpages;
+ int i;
-static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ host->devtype_data->enable_hwecc(chip, ecc);
- /*
- * 1-Bit errors are automatically corrected in HW. No need for
- * additional correction. 2-Bit errors cannot be corrected by
- * HW ECC, so we need to return failure
- */
- uint16_t ecc_status = get_ecc_status_v1(host);
+ host->devtype_data->send_cmd(host, NAND_CMD_READ0, false);
+ mxc_do_addr_cycle(mtd, 0, page);
+
+ if (mtd->writesize > 512)
+ host->devtype_data->send_cmd(host, NAND_CMD_READSTART, true);
+
+ no_subpages = mtd->writesize >> 9;
+
+ for (i = 0; i < no_subpages; i++) {
+ uint16_t ecc_stats;
+
+ /* NANDFC buffer 0 is used for page read/write */
+ writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR);
+
+ writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, true);
+
+ ecc_stats = get_ecc_status_v1(host);
+
+ ecc_stats >>= 2;
- if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
- dev_dbg(host->dev, "HWECC uncorrectable 2-bit ECC error\n");
- return -EBADMSG;
+ if (buf && ecc) {
+ switch (ecc_stats & 0x3) {
+ case 0:
+ default:
+ break;
+ case 1:
+ mtd->ecc_stats.corrected++;
+ bitflips_corrected = 1;
+ break;
+ case 2:
+ mtd->ecc_stats.failed++;
+ break;
+ }
+ }
}
- return 0;
+ if (buf)
+ memcpy32_fromio(buf, host->main_area0, mtd->writesize);
+ if (oob)
+ copy_spare(mtd, true, oob);
+
+ return bitflips_corrected;
}
-static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
+static int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf,
+ void *oob, bool ecc, int page)
{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ unsigned int max_bitflips = 0;
u32 ecc_stat, err;
- int no_subpages = 1;
- int ret = 0;
+ int no_subpages;
u8 ecc_bit_mask, err_limit;
+ host->devtype_data->enable_hwecc(chip, ecc);
+
+ host->devtype_data->send_cmd(host, NAND_CMD_READ0, false);
+ mxc_do_addr_cycle(mtd, 0, page);
+
+ if (mtd->writesize > 512)
+ host->devtype_data->send_cmd(host,
+ NAND_CMD_READSTART, true);
+
+ host->devtype_data->send_page(mtd, NFC_OUTPUT);
+
+ if (buf)
+ memcpy32_fromio(buf, host->main_area0, mtd->writesize);
+ if (oob)
+ copy_spare(mtd, true, oob);
+
ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf;
err_limit = (host->eccsize == 4) ? 0x4 : 0x8;
@@ -634,25 +819,99 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
do {
err = ecc_stat & ecc_bit_mask;
if (err > err_limit) {
- dev_dbg(host->dev, "UnCorrectable RS-ECC Error\n");
- return -EBADMSG;
+ mtd->ecc_stats.failed++;
} else {
- ret += err;
+ mtd->ecc_stats.corrected += err;
+ max_bitflips = max_t(unsigned int, max_bitflips, err);
}
+
ecc_stat >>= 4;
} while (--no_subpages);
- dev_dbg(host->dev, "%d Symbol Correctable RS-ECC Error\n", ret);
+ return max_bitflips;
+}
- return ret;
+static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ void *oob_buf;
+
+ if (oob_required)
+ oob_buf = chip->oob_poi;
+ else
+ oob_buf = NULL;
+
+ return host->devtype_data->read_page(chip, buf, oob_buf, 1, page);
+}
+
+static int mxc_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ void *oob_buf;
+
+ if (oob_required)
+ oob_buf = chip->oob_poi;
+ else
+ oob_buf = NULL;
+
+ return host->devtype_data->read_page(chip, buf, oob_buf, 0, page);
+}
+
+static int mxc_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ return host->devtype_data->read_page(chip, NULL, chip->oob_poi, 0,
+ page);
}
-static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
+static int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf,
+ bool ecc, int page)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ host->devtype_data->enable_hwecc(chip, ecc);
+
+ host->devtype_data->send_cmd(host, NAND_CMD_SEQIN, false);
+ mxc_do_addr_cycle(mtd, 0, page);
+
+ memcpy32_toio(host->main_area0, buf, mtd->writesize);
+ copy_spare(mtd, false, chip->oob_poi);
+
+ host->devtype_data->send_page(mtd, NFC_INPUT);
+ host->devtype_data->send_cmd(host, NAND_CMD_PAGEPROG, true);
+ mxc_do_addr_cycle(mtd, 0, page);
+
return 0;
}
+static int mxc_nand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ return mxc_nand_write_page(chip, buf, true, page);
+}
+
+static int mxc_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ return mxc_nand_write_page(chip, buf, false, page);
+}
+
+static int mxc_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+ memset(host->data_buf, 0xff, mtd->writesize);
+
+ return mxc_nand_write_page(chip, host->data_buf, false, page);
+}
+
static u_char mxc_nand_read_byte(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
@@ -772,109 +1031,6 @@ static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
}
-/*
- * The controller splits a page into data chunks of 512 bytes + partial oob.
- * There are writesize / 512 such chunks, the size of the partial oob parts is
- * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then
- * contains additionally the byte lost by rounding (if any).
- * This function handles the needed shuffling between host->data_buf (which
- * holds a page in natural order, i.e. writesize bytes data + oobsize bytes
- * spare) and the NFC buffer.
- */
-static void copy_spare(struct mtd_info *mtd, bool bfrom)
-{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(this);
- u16 i, oob_chunk_size;
- u16 num_chunks = mtd->writesize / 512;
-
- u8 *d = host->data_buf + mtd->writesize;
- u8 __iomem *s = host->spare0;
- u16 sparebuf_size = host->devtype_data->spare_len;
-
- /* size of oob chunk for all but possibly the last one */
- oob_chunk_size = (host->used_oobsize / num_chunks) & ~1;
-
- if (bfrom) {
- for (i = 0; i < num_chunks - 1; i++)
- memcpy16_fromio(d + i * oob_chunk_size,
- s + i * sparebuf_size,
- oob_chunk_size);
-
- /* the last chunk */
- memcpy16_fromio(d + i * oob_chunk_size,
- s + i * sparebuf_size,
- host->used_oobsize - i * oob_chunk_size);
- } else {
- for (i = 0; i < num_chunks - 1; i++)
- memcpy16_toio(&s[i * sparebuf_size],
- &d[i * oob_chunk_size],
- oob_chunk_size);
-
- /* the last chunk */
- memcpy16_toio(&s[i * sparebuf_size],
- &d[i * oob_chunk_size],
- host->used_oobsize - i * oob_chunk_size);
- }
-}
-
-/*
- * MXC NANDFC can only perform full page+spare or spare-only read/write. When
- * the upper layers perform a read/write buf operation, the saved column address
- * is used to index into the full page. So usually this function is called with
- * column == 0 (unless no column cycle is needed indicated by column == -1)
- */
-static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
-{
- struct nand_chip *nand_chip = mtd_to_nand(mtd);
- struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
-
- /* Write out column address, if necessary */
- if (column != -1) {
- host->devtype_data->send_addr(host, column & 0xff,
- page_addr == -1);
- if (mtd->writesize > 512)
- /* another col addr cycle for 2k page */
- host->devtype_data->send_addr(host,
- (column >> 8) & 0xff,
- false);
- }
-
- /* Write out page address, if necessary */
- if (page_addr != -1) {
- /* paddr_0 - p_addr_7 */
- host->devtype_data->send_addr(host, (page_addr & 0xff), false);
-
- if (mtd->writesize > 512) {
- if (mtd->size >= 0x10000000) {
- /* paddr_8 - paddr_15 */
- host->devtype_data->send_addr(host,
- (page_addr >> 8) & 0xff,
- false);
- host->devtype_data->send_addr(host,
- (page_addr >> 16) & 0xff,
- true);
- } else
- /* paddr_8 - paddr_15 */
- host->devtype_data->send_addr(host,
- (page_addr >> 8) & 0xff, true);
- } else {
- if (nand_chip->options & NAND_ROW_ADDR_3) {
- /* paddr_8 - paddr_15 */
- host->devtype_data->send_addr(host,
- (page_addr >> 8) & 0xff,
- false);
- host->devtype_data->send_addr(host,
- (page_addr >> 16) & 0xff,
- true);
- } else
- /* paddr_8 - paddr_15 */
- host->devtype_data->send_addr(host,
- (page_addr >> 8) & 0xff, true);
- }
- }
-}
-
#define MXC_V1_ECCBYTES 5
static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
@@ -1235,57 +1391,6 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
mxc_do_addr_cycle(mtd, column, page_addr);
break;
- case NAND_CMD_READ0:
- case NAND_CMD_READOOB:
- if (command == NAND_CMD_READ0)
- host->buf_start = column;
- else
- host->buf_start = column + mtd->writesize;
-
- command = NAND_CMD_READ0; /* only READ0 is valid */
-
- host->devtype_data->send_cmd(host, command, false);
- WARN_ONCE(column < 0,
- "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
- command, column, page_addr);
- mxc_do_addr_cycle(mtd, 0, page_addr);
-
- if (mtd->writesize > 512)
- host->devtype_data->send_cmd(host,
- NAND_CMD_READSTART, true);
-
- host->devtype_data->send_page(mtd, NFC_OUTPUT);
-
- memcpy32_fromio(host->data_buf, host->main_area0,
- mtd->writesize);
- copy_spare(mtd, true);
- break;
-
- case NAND_CMD_SEQIN:
- if (column >= mtd->writesize)
- /* call ourself to read a page */
- mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr);
-
- host->buf_start = column;
-
- host->devtype_data->send_cmd(host, command, false);
- WARN_ONCE(column < -1,
- "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
- command, column, page_addr);
- mxc_do_addr_cycle(mtd, 0, page_addr);
- break;
-
- case NAND_CMD_PAGEPROG:
- memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
- copy_spare(mtd, false);
- host->devtype_data->send_page(mtd, NFC_INPUT);
- host->devtype_data->send_cmd(host, command, true);
- WARN_ONCE(column != -1 || page_addr != -1,
- "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
- command, column, page_addr);
- mxc_do_addr_cycle(mtd, column, page_addr);
- break;
-
case NAND_CMD_READID:
host->devtype_data->send_cmd(host, command, true);
mxc_do_addr_cycle(mtd, column, page_addr);
@@ -1316,19 +1421,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
}
}
-static int mxc_nand_onfi_set_features(struct mtd_info *mtd,
- struct nand_chip *chip, int addr,
- u8 *subfeature_param)
+static int mxc_nand_set_features(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, u8 *subfeature_param)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
int i;
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
-
host->buf_start = 0;
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
@@ -1342,19 +1441,13 @@ static int mxc_nand_onfi_set_features(struct mtd_info *mtd,
return 0;
}
-static int mxc_nand_onfi_get_features(struct mtd_info *mtd,
- struct nand_chip *chip, int addr,
- u8 *subfeature_param)
+static int mxc_nand_get_features(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, u8 *subfeature_param)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
int i;
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
-
host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
mxc_do_addr_cycle(mtd, addr, -1);
host->devtype_data->send_page(mtd, NFC_OUTPUT);
@@ -1397,6 +1490,7 @@ static struct nand_bbt_descr bbt_mirror_descr = {
/* v1 + irqpending_quirk: i.MX21 */
static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
.preset = preset_v1,
+ .read_page = mxc_nand_read_page_v1,
.send_cmd = send_cmd_v1_v2,
.send_addr = send_addr_v1_v2,
.send_page = send_page_v1,
@@ -1407,7 +1501,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
.get_ecc_status = get_ecc_status_v1,
.ooblayout = &mxc_v1_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v1_v3,
- .correct_data = mxc_nand_correct_data_v1,
+ .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
.irqpending_quirk = 1,
.needs_ip = 0,
.regs_offset = 0xe00,
@@ -1420,6 +1514,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
/* v1 + !irqpending_quirk: i.MX27, i.MX31 */
static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
.preset = preset_v1,
+ .read_page = mxc_nand_read_page_v1,
.send_cmd = send_cmd_v1_v2,
.send_addr = send_addr_v1_v2,
.send_page = send_page_v1,
@@ -1430,7 +1525,7 @@ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
.get_ecc_status = get_ecc_status_v1,
.ooblayout = &mxc_v1_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v1_v3,
- .correct_data = mxc_nand_correct_data_v1,
+ .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
.irqpending_quirk = 0,
.needs_ip = 0,
.regs_offset = 0xe00,
@@ -1444,6 +1539,7 @@ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
/* v21: i.MX25, i.MX35 */
static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
.preset = preset_v2,
+ .read_page = mxc_nand_read_page_v2_v3,
.send_cmd = send_cmd_v1_v2,
.send_addr = send_addr_v1_v2,
.send_page = send_page_v2,
@@ -1454,8 +1550,8 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
.get_ecc_status = get_ecc_status_v2,
.ooblayout = &mxc_v2_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v2,
- .correct_data = mxc_nand_correct_data_v2_v3,
.setup_data_interface = mxc_nand_v2_setup_data_interface,
+ .enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
.irqpending_quirk = 0,
.needs_ip = 0,
.regs_offset = 0x1e00,
@@ -1469,6 +1565,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
/* v3.2a: i.MX51 */
static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
.preset = preset_v3,
+ .read_page = mxc_nand_read_page_v2_v3,
.send_cmd = send_cmd_v3,
.send_addr = send_addr_v3,
.send_page = send_page_v3,
@@ -1479,7 +1576,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
.get_ecc_status = get_ecc_status_v3,
.ooblayout = &mxc_v2_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v1_v3,
- .correct_data = mxc_nand_correct_data_v2_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v3,
.irqpending_quirk = 0,
.needs_ip = 1,
.regs_offset = 0,
@@ -1494,6 +1591,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
/* v3.2b: i.MX53 */
static const struct mxc_nand_devtype_data imx53_nand_devtype_data = {
.preset = preset_v3,
+ .read_page = mxc_nand_read_page_v2_v3,
.send_cmd = send_cmd_v3,
.send_addr = send_addr_v3,
.send_page = send_page_v3,
@@ -1504,7 +1602,7 @@ static const struct mxc_nand_devtype_data imx53_nand_devtype_data = {
.get_ecc_status = get_ecc_status_v3,
.ooblayout = &mxc_v2_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v1_v3,
- .correct_data = mxc_nand_correct_data_v2_v3,
+ .enable_hwecc = mxc_nand_enable_hwecc_v3,
.irqpending_quirk = 0,
.needs_ip = 1,
.regs_offset = 0,
@@ -1642,8 +1740,8 @@ static int mxcnd_probe(struct platform_device *pdev)
this->read_word = mxc_nand_read_word;
this->write_buf = mxc_nand_write_buf;
this->read_buf = mxc_nand_read_buf;
- this->onfi_set_features = mxc_nand_onfi_set_features;
- this->onfi_get_features = mxc_nand_onfi_get_features;
+ this->set_features = mxc_nand_set_features;
+ this->get_features = mxc_nand_get_features;
host->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk))
@@ -1751,9 +1849,12 @@ static int mxcnd_probe(struct platform_device *pdev)
switch (this->ecc.mode) {
case NAND_ECC_HW:
- this->ecc.calculate = mxc_nand_calculate_ecc;
- this->ecc.hwctl = mxc_nand_enable_hwecc;
- this->ecc.correct = host->devtype_data->correct_data;
+ this->ecc.read_page = mxc_nand_read_page;
+ this->ecc.read_page_raw = mxc_nand_read_page_raw;
+ this->ecc.read_oob = mxc_nand_read_oob;
+ this->ecc.write_page = mxc_nand_write_page_ecc;
+ this->ecc.write_page_raw = mxc_nand_write_page_raw;
+ this->ecc.write_oob = mxc_nand_write_oob;
break;
case NAND_ECC_SOFT:
@@ -1810,15 +1911,18 @@ static int mxcnd_probe(struct platform_device *pdev)
goto escan;
/* Register the partitions */
- mtd_device_parse_register(mtd, part_probes,
- NULL,
- host->pdata.parts,
- host->pdata.nr_parts);
+ err = mtd_device_parse_register(mtd, part_probes, NULL,
+ host->pdata.parts,
+ host->pdata.nr_parts);
+ if (err)
+ goto cleanup_nand;
platform_set_drvdata(pdev, host);
return 0;
+cleanup_nand:
+ nand_cleanup(this);
escan:
if (host->clk_act)
clk_disable_unprepare(host->clk);
diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/raw/nand_amd.c
index 22f060f38123..22f060f38123 100644
--- a/drivers/mtd/nand/nand_amd.c
+++ b/drivers/mtd/nand/raw/nand_amd.c
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index e70ca16a5118..72f3a89da513 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -349,7 +349,7 @@ static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
* 8-bits of the data bus. During address transfers, the host shall
* set the upper 8-bits of the data bus to 00h.
*
- * One user of the write_byte callback is nand_onfi_set_features. The
+ * One user of the write_byte callback is nand_set_features. The
* four parameters are specified to be written to I/O[7:0], but this is
* neither an address nor a command transfer. Let's assume a 0 on the
* upper I/O lines is OK.
@@ -527,7 +527,6 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
/* Attempt erase before marking OOB */
memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = mtd;
einfo.addr = ofs;
einfo.len = 1ULL << chip->phys_erase_shift;
nand_erase_nand(mtd, &einfo, 0);
@@ -1160,6 +1159,60 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
return status;
}
+static bool nand_supports_get_features(struct nand_chip *chip, int addr)
+{
+ return (chip->parameters.supports_set_get_features &&
+ test_bit(addr, chip->parameters.get_feature_list));
+}
+
+static bool nand_supports_set_features(struct nand_chip *chip, int addr)
+{
+ return (chip->parameters.supports_set_get_features &&
+ test_bit(addr, chip->parameters.set_feature_list));
+}
+
+/**
+ * nand_get_features - wrapper to perform a GET_FEATURE
+ * @chip: NAND chip info structure
+ * @addr: feature address
+ * @subfeature_param: the subfeature parameters, a four bytes array
+ *
+ * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the
+ * operation cannot be handled.
+ */
+int nand_get_features(struct nand_chip *chip, int addr,
+ u8 *subfeature_param)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (!nand_supports_get_features(chip, addr))
+ return -ENOTSUPP;
+
+ return chip->get_features(mtd, chip, addr, subfeature_param);
+}
+EXPORT_SYMBOL_GPL(nand_get_features);
+
+/**
+ * nand_set_features - wrapper to perform a SET_FEATURE
+ * @chip: NAND chip info structure
+ * @addr: feature address
+ * @subfeature_param: the subfeature parameters, a four bytes array
+ *
+ * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the
+ * operation cannot be handled.
+ */
+int nand_set_features(struct nand_chip *chip, int addr,
+ u8 *subfeature_param)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ if (!nand_supports_set_features(chip, addr))
+ return -ENOTSUPP;
+
+ return chip->set_features(mtd, chip, addr, subfeature_param);
+}
+EXPORT_SYMBOL_GPL(nand_set_features);
+
/**
* nand_reset_data_interface - Reset data interface and timings
* @chip: The NAND chip
@@ -1215,31 +1268,59 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
{
struct mtd_info *mtd = nand_to_mtd(chip);
+ u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
+ chip->onfi_timing_mode_default,
+ };
int ret;
if (!chip->setup_data_interface)
return 0;
- /*
- * Ensure the timing mode has been changed on the chip side
- * before changing timings on the controller side.
- */
- if (chip->onfi_version &&
- (le16_to_cpu(chip->onfi_params.opt_cmd) &
- ONFI_OPT_CMD_SET_GET_FEATURES)) {
- u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
- chip->onfi_timing_mode_default,
- };
-
- ret = chip->onfi_set_features(mtd, chip,
- ONFI_FEATURE_ADDR_TIMING_MODE,
- tmode_param);
+ /* Change the mode on the chip side (if supported by the NAND chip) */
+ if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
+ chip->select_chip(mtd, chipnr);
+ ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
+ tmode_param);
+ chip->select_chip(mtd, -1);
if (ret)
- goto err;
+ return ret;
}
+ /* Change the mode on the controller side */
ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface);
-err:
+ if (ret)
+ return ret;
+
+ /* Check the mode has been accepted by the chip, if supported */
+ if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE))
+ return 0;
+
+ memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
+ chip->select_chip(mtd, chipnr);
+ ret = nand_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
+ tmode_param);
+ chip->select_chip(mtd, -1);
+ if (ret)
+ goto err_reset_chip;
+
+ if (tmode_param[0] != chip->onfi_timing_mode_default) {
+ pr_warn("timing mode %d not acknowledged by the NAND chip\n",
+ chip->onfi_timing_mode_default);
+ goto err_reset_chip;
+ }
+
+ return 0;
+
+err_reset_chip:
+ /*
+ * Fallback to mode 0 if the chip explicitly did not ack the chosen
+ * timing mode.
+ */
+ nand_reset_data_interface(chip, chipnr);
+ chip->select_chip(mtd, chipnr);
+ nand_reset_op(chip);
+ chip->select_chip(mtd, -1);
+
return ret;
}
@@ -2739,10 +2820,18 @@ int nand_reset(struct nand_chip *chip, int chipnr)
if (ret)
return ret;
- chip->select_chip(mtd, chipnr);
+ /*
+ * A nand_reset_data_interface() put both the NAND chip and the NAND
+ * controller in timings mode 0. If the default mode for this chip is
+ * also 0, no need to proceed to the change again. Plus, at probe time,
+ * nand_setup_data_interface() uses ->set/get_features() which would
+ * fail anyway as the parameter page is not available yet.
+ */
+ if (!chip->onfi_timing_mode_default)
+ return 0;
+
chip->data_interface = saved_data_intf;
ret = nand_setup_data_interface(chip, chipnr);
- chip->select_chip(mtd, -1);
if (ret)
return ret;
@@ -4605,22 +4694,20 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
if (nand_check_wp(mtd)) {
pr_debug("%s: device is write protected!\n",
__func__);
- instr->state = MTD_ERASE_FAILED;
+ ret = -EIO;
goto erase_exit;
}
/* Loop through the pages */
len = instr->len;
- instr->state = MTD_ERASING;
-
while (len) {
/* Check if we have a bad block, we do not erase bad blocks! */
if (nand_block_checkbad(mtd, ((loff_t) page) <<
chip->page_shift, allowbbt)) {
pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
__func__, page);
- instr->state = MTD_ERASE_FAILED;
+ ret = -EIO;
goto erase_exit;
}
@@ -4638,7 +4725,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
if (status) {
pr_debug("%s: failed erase, page 0x%08x\n",
__func__, page);
- instr->state = MTD_ERASE_FAILED;
+ ret = -EIO;
instr->fail_addr =
((loff_t)page << chip->page_shift);
goto erase_exit;
@@ -4655,20 +4742,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
chip->select_chip(mtd, chipnr);
}
}
- instr->state = MTD_ERASE_DONE;
+ ret = 0;
erase_exit:
- ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
-
/* Deselect and wake up anyone waiting on the device */
chip->select_chip(mtd, -1);
nand_release_device(mtd);
- /* Do call back function */
- if (!ret)
- mtd_erase_callback(instr);
-
/* Return more or less happy */
return ret;
}
@@ -4769,44 +4850,35 @@ static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
}
/**
- * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
+ * nand_default_set_features- [REPLACEABLE] set NAND chip features
* @mtd: MTD device structure
* @chip: nand chip info structure
* @addr: feature address.
* @subfeature_param: the subfeature parameters, a four bytes array.
*/
-static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
- int addr, uint8_t *subfeature_param)
+static int nand_default_set_features(struct mtd_info *mtd,
+ struct nand_chip *chip, int addr,
+ uint8_t *subfeature_param)
{
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
-
return nand_set_features_op(chip, addr, subfeature_param);
}
/**
- * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
+ * nand_default_get_features- [REPLACEABLE] get NAND chip features
* @mtd: MTD device structure
* @chip: nand chip info structure
* @addr: feature address.
* @subfeature_param: the subfeature parameters, a four bytes array.
*/
-static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
- int addr, uint8_t *subfeature_param)
+static int nand_default_get_features(struct mtd_info *mtd,
+ struct nand_chip *chip, int addr,
+ uint8_t *subfeature_param)
{
- if (!chip->onfi_version ||
- !(le16_to_cpu(chip->onfi_params.opt_cmd)
- & ONFI_OPT_CMD_SET_GET_FEATURES))
- return -EINVAL;
-
return nand_get_features_op(chip, addr, subfeature_param);
}
/**
- * nand_onfi_get_set_features_notsupp - set/get features stub returning
- * -ENOTSUPP
+ * nand_get_set_features_notsupp - set/get features stub returning -ENOTSUPP
* @mtd: MTD device structure
* @chip: nand chip info structure
* @addr: feature address.
@@ -4815,13 +4887,12 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
* Should be used by NAND controller drivers that do not support the SET/GET
* FEATURES operations.
*/
-int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd,
- struct nand_chip *chip, int addr,
- u8 *subfeature_param)
+int nand_get_set_features_notsupp(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, u8 *subfeature_param)
{
return -ENOTSUPP;
}
-EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp);
+EXPORT_SYMBOL(nand_get_set_features_notsupp);
/**
* nand_suspend - [MTD Interface] Suspend the NAND flash
@@ -4878,10 +4949,10 @@ static void nand_set_defaults(struct nand_chip *chip)
chip->select_chip = nand_select_chip;
/* set for ONFI nand */
- if (!chip->onfi_set_features)
- chip->onfi_set_features = nand_onfi_set_features;
- if (!chip->onfi_get_features)
- chip->onfi_get_features = nand_onfi_get_features;
+ if (!chip->set_features)
+ chip->set_features = nand_default_set_features;
+ if (!chip->get_features)
+ chip->get_features = nand_default_get_features;
/* If called twice, pointers that depend on busw may need to be reset */
if (!chip->read_byte || chip->read_byte == nand_read_byte)
@@ -5021,7 +5092,7 @@ ext_out:
static int nand_flash_detect_onfi(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_onfi_params *p = &chip->onfi_params;
+ struct nand_onfi_params *p;
char id[4];
int i, ret, val;
@@ -5030,14 +5101,23 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
if (ret || strncmp(id, "ONFI", 4))
return 0;
+ /* ONFI chip: allocate a buffer to hold its parameter page */
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
ret = nand_read_param_page_op(chip, 0, NULL, 0);
- if (ret)
- return 0;
+ if (ret) {
+ ret = 0;
+ goto free_onfi_param_page;
+ }
for (i = 0; i < 3; i++) {
ret = nand_read_data_op(chip, p, sizeof(*p), true);
- if (ret)
- return 0;
+ if (ret) {
+ ret = 0;
+ goto free_onfi_param_page;
+ }
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
@@ -5047,31 +5127,33 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
if (i == 3) {
pr_err("Could not find valid ONFI parameter page; aborting\n");
- return 0;
+ goto free_onfi_param_page;
}
/* Check version */
val = le16_to_cpu(p->revision);
if (val & (1 << 5))
- chip->onfi_version = 23;
+ chip->parameters.onfi.version = 23;
else if (val & (1 << 4))
- chip->onfi_version = 22;
+ chip->parameters.onfi.version = 22;
else if (val & (1 << 3))
- chip->onfi_version = 21;
+ chip->parameters.onfi.version = 21;
else if (val & (1 << 2))
- chip->onfi_version = 20;
+ chip->parameters.onfi.version = 20;
else if (val & (1 << 1))
- chip->onfi_version = 10;
+ chip->parameters.onfi.version = 10;
- if (!chip->onfi_version) {
+ if (!chip->parameters.onfi.version) {
pr_info("unsupported ONFI version: %d\n", val);
- return 0;
+ goto free_onfi_param_page;
+ } else {
+ ret = 1;
}
sanitize_string(p->manufacturer, sizeof(p->manufacturer));
sanitize_string(p->model, sizeof(p->model));
- if (!mtd->name)
- mtd->name = p->model;
+ strncpy(chip->parameters.model, p->model,
+ sizeof(chip->parameters.model) - 1);
mtd->writesize = le32_to_cpu(p->byte_per_page);
@@ -5093,14 +5175,14 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun);
chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun);
- if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS)
+ if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS)
chip->options |= NAND_BUSWIDTH_16;
if (p->ecc_bits != 0xff) {
chip->ecc_strength_ds = p->ecc_bits;
chip->ecc_step_ds = 512;
- } else if (chip->onfi_version >= 21 &&
- (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
+ } else if (chip->parameters.onfi.version >= 21 &&
+ (le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
/*
* The nand_flash_detect_ext_param_page() uses the
@@ -5118,7 +5200,28 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
pr_warn("Could not retrieve ONFI ECC requirements\n");
}
- return 1;
+ /* Save some parameters from the parameter page for future use */
+ if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) {
+ chip->parameters.supports_set_get_features = true;
+ bitmap_set(chip->parameters.get_feature_list,
+ ONFI_FEATURE_ADDR_TIMING_MODE, 1);
+ bitmap_set(chip->parameters.set_feature_list,
+ ONFI_FEATURE_ADDR_TIMING_MODE, 1);
+ }
+ chip->parameters.onfi.tPROG = le16_to_cpu(p->t_prog);
+ chip->parameters.onfi.tBERS = le16_to_cpu(p->t_bers);
+ chip->parameters.onfi.tR = le16_to_cpu(p->t_r);
+ chip->parameters.onfi.tCCS = le16_to_cpu(p->t_ccs);
+ chip->parameters.onfi.async_timing_mode =
+ le16_to_cpu(p->async_timing_mode);
+ chip->parameters.onfi.vendor_revision =
+ le16_to_cpu(p->vendor_revision);
+ memcpy(chip->parameters.onfi.vendor, p->vendor,
+ sizeof(p->vendor));
+
+free_onfi_param_page:
+ kfree(p);
+ return ret;
}
/*
@@ -5127,8 +5230,9 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
static int nand_flash_detect_jedec(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- struct nand_jedec_params *p = &chip->jedec_params;
+ struct nand_jedec_params *p;
struct jedec_ecc_info *ecc;
+ int jedec_version = 0;
char id[5];
int i, val, ret;
@@ -5137,14 +5241,23 @@ static int nand_flash_detect_jedec(struct nand_chip *chip)
if (ret || strncmp(id, "JEDEC", sizeof(id)))
return 0;
+ /* JEDEC chip: allocate a buffer to hold its parameter page */
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
ret = nand_read_param_page_op(chip, 0x40, NULL, 0);
- if (ret)
- return 0;
+ if (ret) {
+ ret = 0;
+ goto free_jedec_param_page;
+ }
for (i = 0; i < 3; i++) {
ret = nand_read_data_op(chip, p, sizeof(*p), true);
- if (ret)
- return 0;
+ if (ret) {
+ ret = 0;
+ goto free_jedec_param_page;
+ }
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) ==
le16_to_cpu(p->crc))
@@ -5153,25 +5266,25 @@ static int nand_flash_detect_jedec(struct nand_chip *chip)
if (i == 3) {
pr_err("Could not find valid JEDEC parameter page; aborting\n");
- return 0;
+ goto free_jedec_param_page;
}
/* Check version */
val = le16_to_cpu(p->revision);
if (val & (1 << 2))
- chip->jedec_version = 10;
+ jedec_version = 10;
else if (val & (1 << 1))
- chip->jedec_version = 1; /* vendor specific version */
+ jedec_version = 1; /* vendor specific version */
- if (!chip->jedec_version) {
+ if (!jedec_version) {
pr_info("unsupported JEDEC version: %d\n", val);
- return 0;
+ goto free_jedec_param_page;
}
sanitize_string(p->manufacturer, sizeof(p->manufacturer));
sanitize_string(p->model, sizeof(p->model));
- if (!mtd->name)
- mtd->name = p->model;
+ strncpy(chip->parameters.model, p->model,
+ sizeof(chip->parameters.model) - 1);
mtd->writesize = le32_to_cpu(p->byte_per_page);
@@ -5186,7 +5299,7 @@ static int nand_flash_detect_jedec(struct nand_chip *chip)
chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
chip->bits_per_cell = p->bits_per_cell;
- if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS)
+ if (le16_to_cpu(p->features) & JEDEC_FEATURE_16_BIT_BUS)
chip->options |= NAND_BUSWIDTH_16;
/* ECC info */
@@ -5199,7 +5312,9 @@ static int nand_flash_detect_jedec(struct nand_chip *chip)
pr_warn("Invalid codeword size\n");
}
- return 1;
+free_jedec_param_page:
+ kfree(p);
+ return ret;
}
/*
@@ -5358,8 +5473,8 @@ static bool find_full_id_nand(struct nand_chip *chip,
chip->onfi_timing_mode_default =
type->onfi_timing_mode_default;
- if (!mtd->name)
- mtd->name = type->name;
+ strncpy(chip->parameters.model, type->name,
+ sizeof(chip->parameters.model) - 1);
return true;
}
@@ -5498,22 +5613,28 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
}
}
- chip->onfi_version = 0;
+ chip->parameters.onfi.version = 0;
if (!type->name || !type->pagesize) {
/* Check if the chip is ONFI compliant */
- if (nand_flash_detect_onfi(chip))
+ ret = nand_flash_detect_onfi(chip);
+ if (ret < 0)
+ return ret;
+ else if (ret)
goto ident_done;
/* Check if the chip is JEDEC compliant */
- if (nand_flash_detect_jedec(chip))
+ ret = nand_flash_detect_jedec(chip);
+ if (ret < 0)
+ return ret;
+ else if (ret)
goto ident_done;
}
if (!type->name)
return -ENODEV;
- if (!mtd->name)
- mtd->name = type->name;
+ strncpy(chip->parameters.model, type->name,
+ sizeof(chip->parameters.model) - 1);
chip->chipsize = (uint64_t)type->chipsize << 20;
@@ -5526,6 +5647,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
chip->options |= type->options;
ident_done:
+ if (!mtd->name)
+ mtd->name = chip->parameters.model;
if (chip->options & NAND_BUSWIDTH_AUTO) {
WARN_ON(busw & NAND_BUSWIDTH_16);
@@ -5572,17 +5695,8 @@ ident_done:
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
maf_id, dev_id);
-
- if (chip->onfi_version)
- pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
- chip->onfi_params.model);
- else if (chip->jedec_version)
- pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
- chip->jedec_params.model);
- else
- pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
- type->name);
-
+ pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
+ chip->parameters.model);
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
@@ -6474,10 +6588,7 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Enter fastest possible mode on all dies. */
for (i = 0; i < chip->numchips; i++) {
- chip->select_chip(mtd, i);
ret = nand_setup_data_interface(chip, i);
- chip->select_chip(mtd, -1);
-
if (ret)
goto err_nand_manuf_cleanup;
}
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c
index 36092850be2c..d9f4ceff2568 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/raw/nand_bbt.c
@@ -852,7 +852,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
}
memset(&einfo, 0, sizeof(einfo));
- einfo.mtd = mtd;
einfo.addr = to;
einfo.len = 1 << this->bbt_erase_shift;
res = nand_erase_nand(mtd, &einfo, 1);
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c
index 505441c9373b..7f11b68f6db1 100644
--- a/drivers/mtd/nand/nand_bch.c
+++ b/drivers/mtd/nand/raw/nand_bch.c
@@ -95,7 +95,7 @@ int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
errloc[i]);
}
} else if (count < 0) {
- printk(KERN_ERR "ecc unrecoverable error\n");
+ pr_err("ecc unrecoverable error\n");
count = -EBADMSG;
}
return count;
@@ -134,7 +134,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
}
if (!eccsize || !eccbytes) {
- printk(KERN_WARNING "ecc parameters not supplied\n");
+ pr_warn("ecc parameters not supplied\n");
goto fail;
}
@@ -151,8 +151,8 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
/* verify that eccbytes has the expected value */
if (nbc->bch->ecc_bytes != eccbytes) {
- printk(KERN_WARNING "invalid eccbytes %u, should be %u\n",
- eccbytes, nbc->bch->ecc_bytes);
+ pr_warn("invalid eccbytes %u, should be %u\n",
+ eccbytes, nbc->bch->ecc_bytes);
goto fail;
}
@@ -166,7 +166,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
/* sanity checks */
if (8*(eccsize+eccbytes) >= (1 << m)) {
- printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
+ pr_warn("eccsize %u is too large\n", eccsize);
goto fail;
}
@@ -181,7 +181,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
nand->ecc.steps = eccsteps;
nand->ecc.total = eccsteps * eccbytes;
if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
- printk(KERN_WARNING "invalid ecc layout\n");
+ pr_warn("invalid ecc layout\n");
goto fail;
}
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c
index 7613a0388044..8e132edbc5ce 100644
--- a/drivers/mtd/nand/nand_ecc.c
+++ b/drivers/mtd/nand/raw/nand_ecc.c
@@ -2,8 +2,6 @@
* This file contains an ECC algorithm that detects and corrects 1 bit
* errors in a 256 byte block of data.
*
- * drivers/mtd/nand/nand_ecc.c
- *
* Copyright © 2008 Koninklijke Philips Electronics NV.
* Author: Frans Meulenbroeks
*
@@ -30,15 +28,6 @@
*
*/
-/*
- * The STANDALONE macro is useful when running the code outside the kernel
- * e.g. when running the code in a testbed or a benchmark program.
- * When STANDALONE is used, the module related macros are commented out
- * as well as the linux include files.
- * Instead a private definition of mtd_info is given to satisfy the compiler
- * (the code does not use mtd_info, so the code does not care)
- */
-#ifndef STANDALONE
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -46,17 +35,6 @@
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <asm/byteorder.h>
-#else
-#include <stdint.h>
-struct mtd_info;
-#define EXPORT_SYMBOL(x) /* x */
-
-#define MODULE_LICENSE(x) /* x */
-#define MODULE_AUTHOR(x) /* x */
-#define MODULE_DESCRIPTION(x) /* x */
-
-#define pr_err printf
-#endif
/*
* invparity is a 256 byte table that contains the odd parity
diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c
index d542908a0ebb..d542908a0ebb 100644
--- a/drivers/mtd/nand/nand_hynix.c
+++ b/drivers/mtd/nand/raw/nand_hynix.c
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c
index 5423c3bb388e..5423c3bb388e 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/raw/nand_ids.c
diff --git a/drivers/mtd/nand/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c
index d290ff2a6d2f..7ed1f87e742a 100644
--- a/drivers/mtd/nand/nand_macronix.c
+++ b/drivers/mtd/nand/raw/nand_macronix.c
@@ -22,6 +22,19 @@ static int macronix_nand_init(struct nand_chip *chip)
if (nand_is_slc(chip))
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+ /*
+ * MX30LF2G18AC chip does not support using SET/GET_FEATURES to change
+ * the timings unlike what is declared in the parameter page. Unflag
+ * this feature to avoid unnecessary downturns.
+ */
+ if (chip->parameters.supports_set_get_features &&
+ !strcmp("MX30LF2G18AC", chip->parameters.model)) {
+ bitmap_clear(chip->parameters.get_feature_list,
+ ONFI_FEATURE_ADDR_TIMING_MODE, 1);
+ bitmap_clear(chip->parameters.set_feature_list,
+ ONFI_FEATURE_ADDR_TIMING_MODE, 1);
+ }
+
return 0;
}
diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c
index 02e109ae73f1..0af45b134c0c 100644
--- a/drivers/mtd/nand/nand_micron.c
+++ b/drivers/mtd/nand/raw/nand_micron.c
@@ -48,8 +48,7 @@ static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
struct nand_chip *chip = mtd_to_nand(mtd);
u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
- return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY,
- feature);
+ return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature);
}
/*
@@ -57,17 +56,18 @@ static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
*/
static int micron_nand_onfi_init(struct nand_chip *chip)
{
- struct nand_onfi_params *p = &chip->onfi_params;
- struct nand_onfi_vendor_micron *micron = (void *)p->vendor;
+ struct nand_parameters *p = &chip->parameters;
+ struct nand_onfi_vendor_micron *micron = (void *)p->onfi.vendor;
- if (!chip->onfi_version)
- return 0;
-
- if (le16_to_cpu(p->vendor_revision) < 1)
- return 0;
+ if (chip->parameters.onfi.version && p->onfi.vendor_revision) {
+ chip->read_retries = micron->read_retry_options;
+ chip->setup_read_retry = micron_nand_setup_read_retry;
+ }
- chip->read_retries = micron->read_retry_options;
- chip->setup_read_retry = micron_nand_setup_read_retry;
+ if (p->supports_set_get_features) {
+ set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->set_feature_list);
+ set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->get_feature_list);
+ }
return 0;
}
@@ -108,8 +108,7 @@ static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable)
if (enable)
feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN;
- return chip->onfi_set_features(nand_to_mtd(chip), chip,
- ONFI_FEATURE_ON_DIE_ECC, feature);
+ return nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
}
static int
@@ -209,7 +208,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
int ret;
- if (chip->onfi_version == 0)
+ if (!chip->parameters.onfi.version)
return MICRON_ON_DIE_UNSUPPORTED;
if (chip->bits_per_cell != 1)
@@ -219,8 +218,10 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
if (ret)
return MICRON_ON_DIE_UNSUPPORTED;
- chip->onfi_get_features(nand_to_mtd(chip), chip,
- ONFI_FEATURE_ON_DIE_ECC, feature);
+ ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
+ if (ret < 0)
+ return ret;
+
if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0)
return MICRON_ON_DIE_UNSUPPORTED;
@@ -228,8 +229,10 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
if (ret)
return MICRON_ON_DIE_UNSUPPORTED;
- chip->onfi_get_features(nand_to_mtd(chip), chip,
- ONFI_FEATURE_ON_DIE_ECC, feature);
+ ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
+ if (ret < 0)
+ return ret;
+
if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN)
return MICRON_ON_DIE_MANDATORY;
@@ -237,7 +240,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip)
* Some Micron NANDs have an on-die ECC of 4/512, some other
* 8/512. We only support the former.
*/
- if (chip->onfi_params.ecc_bits != 4)
+ if (chip->ecc_strength_ds != 4)
return MICRON_ON_DIE_UNSUPPORTED;
return MICRON_ON_DIE_SUPPORTED;
diff --git a/drivers/mtd/nand/nand_samsung.c b/drivers/mtd/nand/raw/nand_samsung.c
index ef022f62f74c..ef022f62f74c 100644
--- a/drivers/mtd/nand/nand_samsung.c
+++ b/drivers/mtd/nand/raw/nand_samsung.c
diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c
index 9400d039ddbd..7c4e4a371bbc 100644
--- a/drivers/mtd/nand/nand_timings.c
+++ b/drivers/mtd/nand/raw/nand_timings.c
@@ -306,17 +306,17 @@ int onfi_fill_data_interface(struct nand_chip *chip,
* tR, tPROG, tCCS, ...
* These information are part of the ONFI parameter page.
*/
- if (chip->onfi_version) {
- struct nand_onfi_params *params = &chip->onfi_params;
+ if (chip->parameters.onfi.version) {
+ struct nand_parameters *params = &chip->parameters;
struct nand_sdr_timings *timings = &iface->timings.sdr;
/* microseconds -> picoseconds */
- timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog);
- timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers);
- timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r);
+ timings->tPROG_max = 1000000ULL * params->onfi.tPROG;
+ timings->tBERS_max = 1000000ULL * params->onfi.tBERS;
+ timings->tR_max = 1000000ULL * params->onfi.tR;
/* nanoseconds -> picoseconds */
- timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs);
+ timings->tCCS_min = 1000UL * params->onfi.tCCS;
}
return 0;
diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c
index 57df857074e6..ab43f027cd23 100644
--- a/drivers/mtd/nand/nand_toshiba.c
+++ b/drivers/mtd/nand/raw/nand_toshiba.c
@@ -35,6 +35,32 @@ static void toshiba_nand_decode_id(struct nand_chip *chip)
(chip->id.data[5] & 0x7) == 0x6 /* 24nm */ &&
!(chip->id.data[4] & 0x80) /* !BENAND */)
mtd->oobsize = 32 * mtd->writesize >> 9;
+
+ /*
+ * Extract ECC requirements from 6th id byte.
+ * For Toshiba SLC, ecc requrements are as follows:
+ * - 43nm: 1 bit ECC for each 512Byte is required.
+ * - 32nm: 4 bit ECC for each 512Byte is required.
+ * - 24nm: 8 bit ECC for each 512Byte is required.
+ */
+ if (chip->id.len >= 6 && nand_is_slc(chip)) {
+ chip->ecc_step_ds = 512;
+ switch (chip->id.data[5] & 0x7) {
+ case 0x4:
+ chip->ecc_strength_ds = 1;
+ break;
+ case 0x5:
+ chip->ecc_strength_ds = 4;
+ break;
+ case 0x6:
+ chip->ecc_strength_ds = 8;
+ break;
+ default:
+ WARN(1, "Could not get ECC info");
+ chip->ecc_step_ds = 0;
+ break;
+ }
+ }
}
static int toshiba_nand_init(struct nand_chip *chip)
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/raw/nandsim.c
index 44322a363ba5..e027c6f9d327 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/raw/nandsim.c
@@ -23,6 +23,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*/
+#define pr_fmt(fmt) "[nandsim]" fmt
+
#include <linux/init.h>
#include <linux/types.h>
#include <linux/module.h>
@@ -179,20 +181,17 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
/* The largest possible page size */
#define NS_LARGEST_PAGE_SIZE 4096
-/* The prefix for simulator output */
-#define NS_OUTPUT_PREFIX "[nandsim]"
-
/* Simulator's output macros (logging, debugging, warning, error) */
#define NS_LOG(args...) \
- do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0)
+ do { if (log) pr_debug(" log: " args); } while(0)
#define NS_DBG(args...) \
- do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)
+ do { if (dbg) pr_debug(" debug: " args); } while(0)
#define NS_WARN(args...) \
- do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0)
+ do { pr_warn(" warning: " args); } while(0)
#define NS_ERR(args...) \
- do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0)
+ do { pr_err(" error: " args); } while(0)
#define NS_INFO(args...) \
- do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0)
+ do { pr_info(" " args); } while(0)
/* Busy-wait delay macros (microseconds, milliseconds) */
#define NS_UDELAY(us) \
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/raw/ndfc.c
index d8a806894937..d8a806894937 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/raw/ndfc.c
diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/raw/nuc900_nand.c
index af5b32c9a791..af5b32c9a791 100644
--- a/drivers/mtd/nand/nuc900_nand.c
+++ b/drivers/mtd/nand/raw/nuc900_nand.c
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/raw/omap2.c
index 8cdf7d3d8fa7..e50c64adc3c8 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/raw/omap2.c
@@ -2263,12 +2263,15 @@ scan_tail:
err = mtd_device_register(mtd, NULL, 0);
if (err)
- goto return_error;
+ goto cleanup_nand;
platform_set_drvdata(pdev, mtd);
return 0;
+cleanup_nand:
+ nand_cleanup(nand_chip);
+
return_error:
if (!IS_ERR_OR_NULL(info->dma))
dma_release_channel(info->dma);
diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c
index a3f32f939cc1..a3f32f939cc1 100644
--- a/drivers/mtd/nand/omap_elm.c
+++ b/drivers/mtd/nand/raw/omap_elm.c
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c
index 5a5aa1f07d07..7825fd3ce66b 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/raw/orion_nand.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand/orion_nand.c
- *
* NAND support for Marvell Orion SoC platforms
*
* Tzachi Perelstein <tzachi@marvell.com>
diff --git a/drivers/mtd/nand/oxnas_nand.c b/drivers/mtd/nand/raw/oxnas_nand.c
index d649d5944826..d649d5944826 100644
--- a/drivers/mtd/nand/oxnas_nand.c
+++ b/drivers/mtd/nand/raw/oxnas_nand.c
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c
index a47a7e4bd25a..a47a7e4bd25a 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/raw/pasemi_nand.c
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c
index 925a1323604d..925a1323604d 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/raw/plat_nand.c
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
index 563b759ffca6..b554fb6e609c 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -2651,8 +2651,8 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc,
chip->read_byte = qcom_nandc_read_byte;
chip->read_buf = qcom_nandc_read_buf;
chip->write_buf = qcom_nandc_write_buf;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
/*
* the bad block marker is readable only when we read the last codeword
diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/raw/r852.c
index 595635b9e9de..dcdeb0660e5e 100644
--- a/drivers/mtd/nand/r852.c
+++ b/drivers/mtd/nand/raw/r852.c
@@ -7,6 +7,9 @@
* published by the Free Software Foundation.
*/
+#define DRV_NAME "r852"
+#define pr_fmt(fmt) DRV_NAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/jiffies.h>
@@ -932,7 +935,7 @@ static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
&dev->card_detect_work, 0);
- printk(KERN_NOTICE DRV_NAME ": driver loaded successfully\n");
+ pr_notice("driver loaded successfully\n");
return 0;
error10:
diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/raw/r852.h
index 8713c57f6207..1eed2fc2fa42 100644
--- a/drivers/mtd/nand/r852.h
+++ b/drivers/mtd/nand/raw/r852.h
@@ -144,17 +144,14 @@ struct r852_device {
uint8_t ctlreg; /* cached contents of control reg */
};
-#define DRV_NAME "r852"
-
-
#define dbg(format, ...) \
if (debug) \
- printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__)
+ pr_debug(format "\n", ## __VA_ARGS__)
#define dbg_verbose(format, ...) \
if (debug > 1) \
- printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__)
+ pr_debug(format "\n", ## __VA_ARGS__)
#define message(format, ...) \
- printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__)
+ pr_info(format "\n", ## __VA_ARGS__)
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c
index 4c383eeec6f6..1bc0458063d8 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/raw/s3c2410.c
@@ -1,5 +1,4 @@
-/* linux/drivers/mtd/nand/s3c2410.c
- *
+/*
* Copyright © 2004-2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
@@ -125,13 +124,11 @@ struct s3c2410_nand_info;
* @chip: The NAND chip information.
* @set: The platform information supplied for this set of NAND chips.
* @info: Link back to the hardware information.
- * @scan_res: The result from calling nand_scan_ident().
*/
struct s3c2410_nand_mtd {
struct nand_chip chip;
struct s3c2410_nand_set *set;
struct s3c2410_nand_info *info;
- int scan_res;
};
enum s3c_cpu_type {
@@ -1164,17 +1161,19 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
mtd->dev.parent = &pdev->dev;
s3c2410_nand_init_chip(info, nmtd, sets);
- nmtd->scan_res = nand_scan_ident(mtd,
- (sets) ? sets->nr_chips : 1,
- NULL);
+ err = nand_scan_ident(mtd, (sets) ? sets->nr_chips : 1, NULL);
+ if (err)
+ goto exit_error;
- if (nmtd->scan_res == 0) {
- err = s3c2410_nand_update_chip(info, nmtd);
- if (err < 0)
- goto exit_error;
- nand_scan_tail(mtd);
- s3c2410_nand_add_partition(info, nmtd, sets);
- }
+ err = s3c2410_nand_update_chip(info, nmtd);
+ if (err < 0)
+ goto exit_error;
+
+ err = nand_scan_tail(mtd);
+ if (err)
+ goto exit_error;
+
+ s3c2410_nand_add_partition(info, nmtd, sets);
if (sets != NULL)
sets++;
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c
index c4e7755448e6..c7abceffcc40 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/raw/sh_flctl.c
@@ -877,7 +877,7 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
else if (!flctl->seqin_column)
execmd_write_page_sector(mtd);
else
- printk(KERN_ERR "Invalid address !?\n");
+ pr_err("Invalid address !?\n");
break;
}
set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN);
@@ -1180,8 +1180,8 @@ static int flctl_probe(struct platform_device *pdev)
nand->read_buf = flctl_read_buf;
nand->select_chip = flctl_select_chip;
nand->cmdfunc = flctl_cmdfunc;
- nand->onfi_set_features = nand_onfi_get_set_features_notsupp;
- nand->onfi_get_features = nand_onfi_get_set_features_notsupp;
+ nand->set_features = nand_get_set_features_notsupp;
+ nand->get_features = nand_get_set_features_notsupp;
if (pdata->flcmncr_val & SEL_16BIT)
nand->options |= NAND_BUSWIDTH_16;
@@ -1214,9 +1214,13 @@ static int flctl_probe(struct platform_device *pdev)
goto err_chip;
ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
+ if (ret)
+ goto cleanup_nand;
return 0;
+cleanup_nand:
+ nand_cleanup(nand);
err_chip:
flctl_release_dma(flctl);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c
index f59c455d9f51..e93df02c825e 100644
--- a/drivers/mtd/nand/sharpsl.c
+++ b/drivers/mtd/nand/raw/sharpsl.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand/sharpsl.c
- *
* Copyright (C) 2004 Richard Purdie
* Copyright (C) 2008 Dmitry Baryshkov
*
diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/raw/sm_common.c
index c378705c6e2b..7f5044a79f01 100644
--- a/drivers/mtd/nand/sm_common.c
+++ b/drivers/mtd/nand/raw/sm_common.c
@@ -119,9 +119,8 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
ret = mtd_write_oob(mtd, ofs, &ops);
if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
- printk(KERN_NOTICE
- "sm_common: can't mark sector at %i as bad\n",
- (int)ofs);
+ pr_notice("sm_common: can't mark sector at %i as bad\n",
+ (int)ofs);
return -EIO;
}
diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/raw/sm_common.h
index 1581671b05ae..1581671b05ae 100644
--- a/drivers/mtd/nand/sm_common.h
+++ b/drivers/mtd/nand/raw/sm_common.h
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c
index 575997d0ef8a..9824a9923583 100644
--- a/drivers/mtd/nand/socrates_nand.c
+++ b/drivers/mtd/nand/raw/socrates_nand.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand/socrates_nand.c
- *
* Copyright © 2008 Ilya Yanok, Emcraft Systems
*
*
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index f5a55c63935c..aad42812a353 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -1475,92 +1475,18 @@ pio_fallback:
return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page);
}
-static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- uint8_t *buf, int oob_required,
- int page)
-{
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- unsigned int max_bitflips = 0;
- int ret, i, cur_off = 0;
- bool raw_mode = false;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
-
- sunxi_nfc_hw_ecc_enable(mtd);
-
- for (i = 0; i < ecc->steps; i++) {
- int data_off = i * (ecc->size + ecc->bytes + 4);
- int oob_off = data_off + ecc->size;
- u8 *data = buf + (i * ecc->size);
- u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
-
- ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
- oob_off, &cur_off,
- &max_bitflips, !i,
- oob_required,
- page);
- if (ret < 0)
- return ret;
- else if (ret)
- raw_mode = true;
- }
-
- if (oob_required)
- sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
- !raw_mode, page);
-
- sunxi_nfc_hw_ecc_disable(mtd);
-
- return max_bitflips;
-}
-
-static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- const uint8_t *buf,
- int oob_required, int page)
-{
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- int ret, i, cur_off = 0;
-
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- sunxi_nfc_hw_ecc_enable(mtd);
-
- for (i = 0; i < ecc->steps; i++) {
- int data_off = i * (ecc->size + ecc->bytes + 4);
- int oob_off = data_off + ecc->size;
- const u8 *data = buf + (i * ecc->size);
- const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
-
- ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off,
- oob, oob_off, &cur_off,
- false, page);
- if (ret)
- return ret;
- }
-
- if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
- sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
- &cur_off, page);
-
- sunxi_nfc_hw_ecc_disable(mtd);
-
- return nand_prog_page_end_op(chip);
-}
-
-static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd,
- struct nand_chip *chip,
- int page)
+static int sunxi_nfc_hw_ecc_read_oob(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ int page)
{
chip->pagebuf = -1;
return chip->ecc.read_page(mtd, chip, chip->data_buf, 1, page);
}
-static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd,
- struct nand_chip *chip,
- int page)
+static int sunxi_nfc_hw_ecc_write_oob(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ int page)
{
int ret;
@@ -1801,9 +1727,14 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
.free = sunxi_nand_ooblayout_free,
};
-static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
- struct nand_ecc_ctrl *ecc,
- struct device_node *np)
+static void sunxi_nand_hw_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
+{
+ kfree(ecc->priv);
+}
+
+static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc,
+ struct device_node *np)
{
static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
struct nand_chip *nand = mtd_to_nand(mtd);
@@ -1889,37 +1820,11 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
goto err;
}
- ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob;
- ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob;
+ ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
+ ecc->write_oob = sunxi_nfc_hw_ecc_write_oob;
mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
ecc->priv = data;
- return 0;
-
-err:
- kfree(data);
-
- return ret;
-}
-
-static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
-{
- kfree(ecc->priv);
-}
-
-static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
- struct nand_ecc_ctrl *ecc,
- struct device_node *np)
-{
- struct nand_chip *nand = mtd_to_nand(mtd);
- struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
- struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
- int ret;
-
- ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
- if (ret)
- return ret;
-
if (nfc->dmac) {
ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
@@ -1937,33 +1842,18 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
ecc->write_oob_raw = nand_write_oob_std;
return 0;
-}
-
-static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
- struct nand_ecc_ctrl *ecc,
- struct device_node *np)
-{
- int ret;
-
- ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
- if (ret)
- return ret;
- ecc->prepad = 4;
- ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;
- ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;
- ecc->read_oob_raw = nand_read_oob_syndrome;
- ecc->write_oob_raw = nand_write_oob_syndrome;
+err:
+ kfree(data);
- return 0;
+ return ret;
}
static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
{
switch (ecc->mode) {
case NAND_ECC_HW:
- case NAND_ECC_HW_SYNDROME:
- sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc);
+ sunxi_nand_hw_ecc_ctrl_cleanup(ecc);
break;
case NAND_ECC_NONE:
default:
@@ -1991,11 +1881,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
if (ret)
return ret;
break;
- case NAND_ECC_HW_SYNDROME:
- ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc, np);
- if (ret)
- return ret;
- break;
case NAND_ECC_NONE:
case NAND_ECC_SOFT:
break;
diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c
index c5bee00b7f5e..f54518ffb36a 100644
--- a/drivers/mtd/nand/tango_nand.c
+++ b/drivers/mtd/nand/raw/tango_nand.c
@@ -591,8 +591,10 @@ static int chip_init(struct device *dev, struct device_node *np)
tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE);
err = mtd_device_register(mtd, NULL, 0);
- if (err)
+ if (err) {
+ nand_cleanup(chip);
return err;
+ }
nfc->chips[cs] = tchip;
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c
index dcaa924502de..dcaa924502de 100644
--- a/drivers/mtd/nand/tmio_nand.c
+++ b/drivers/mtd/nand/raw/tmio_nand.c
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c
index b567d212fe7d..b567d212fe7d 100644
--- a/drivers/mtd/nand/txx9ndfmc.c
+++ b/drivers/mtd/nand/raw/txx9ndfmc.c
diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c
index f367144f3c6f..d5a22fc96878 100644
--- a/drivers/mtd/nand/vf610_nfc.c
+++ b/drivers/mtd/nand/raw/vf610_nfc.c
@@ -36,6 +36,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/swab.h>
#define DRV_NAME "vf610_nfc"
@@ -59,20 +60,21 @@
#define OOB_64 0x0040
#define OOB_MAX 0x0100
-/*
- * NFC_CMD2[CODE] values. See section:
- * - 31.4.7 Flash Command Code Description, Vybrid manual
- * - 23.8.6 Flash Command Sequencer, MPC5125 manual
- *
- * Briefly these are bitmasks of controller cycles.
- */
-#define READ_PAGE_CMD_CODE 0x7EE0
-#define READ_ONFI_PARAM_CMD_CODE 0x4860
-#define PROGRAM_PAGE_CMD_CODE 0x7FC0
-#define ERASE_CMD_CODE 0x4EC0
-#define READ_ID_CMD_CODE 0x4804
-#define RESET_CMD_CODE 0x4040
-#define STATUS_READ_CMD_CODE 0x4068
+/* NFC_CMD2[CODE] controller cycle bit masks */
+#define COMMAND_CMD_BYTE1 BIT(14)
+#define COMMAND_CAR_BYTE1 BIT(13)
+#define COMMAND_CAR_BYTE2 BIT(12)
+#define COMMAND_RAR_BYTE1 BIT(11)
+#define COMMAND_RAR_BYTE2 BIT(10)
+#define COMMAND_RAR_BYTE3 BIT(9)
+#define COMMAND_NADDR_BYTES(x) GENMASK(13, 13 - (x) + 1)
+#define COMMAND_WRITE_DATA BIT(8)
+#define COMMAND_CMD_BYTE2 BIT(7)
+#define COMMAND_RB_HANDSHAKE BIT(6)
+#define COMMAND_READ_DATA BIT(5)
+#define COMMAND_CMD_BYTE3 BIT(4)
+#define COMMAND_READ_STATUS BIT(3)
+#define COMMAND_READ_ID BIT(2)
/* NFC ECC mode define */
#define ECC_BYPASS 0
@@ -97,10 +99,13 @@
/* NFC_COL_ADDR Field */
#define COL_ADDR_MASK 0x0000FFFF
#define COL_ADDR_SHIFT 0
+#define COL_ADDR(pos, val) (((val) & 0xFF) << (8 * (pos)))
/* NFC_ROW_ADDR Field */
#define ROW_ADDR_MASK 0x00FFFFFF
#define ROW_ADDR_SHIFT 0
+#define ROW_ADDR(pos, val) (((val) & 0xFF) << (8 * (pos)))
+
#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000
#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28
#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000
@@ -142,13 +147,6 @@
#define ECC_STATUS_MASK 0x80
#define ECC_STATUS_ERR_COUNT 0x3F
-enum vf610_nfc_alt_buf {
- ALT_BUF_DATA = 0,
- ALT_BUF_ID = 1,
- ALT_BUF_STAT = 2,
- ALT_BUF_ONFI = 3,
-};
-
enum vf610_nfc_variant {
NFC_VFC610 = 1,
};
@@ -158,13 +156,15 @@ struct vf610_nfc {
struct device *dev;
void __iomem *regs;
struct completion cmd_done;
- uint buf_offset;
- int write_sz;
/* Status and ID are in alternate locations. */
- enum vf610_nfc_alt_buf alt_buf;
enum vf610_nfc_variant variant;
struct clk *clk;
- bool use_hw_ecc;
+ /*
+ * Indicate that user data is accessed (full page/oob). This is
+ * useful to indicate the driver whether to swap byte endianness.
+ * See comments in vf610_nfc_rd_from_sram/vf610_nfc_wr_to_sram.
+ */
+ bool data_access;
u32 ecc_mode;
};
@@ -173,6 +173,11 @@ static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd)
return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip);
}
+static inline struct vf610_nfc *chip_to_nfc(struct nand_chip *chip)
+{
+ return container_of(chip, struct vf610_nfc, chip);
+}
+
static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg)
{
return readl(nfc->regs + reg);
@@ -200,18 +205,84 @@ static inline void vf610_nfc_set_field(struct vf610_nfc *nfc, u32 reg,
(vf610_nfc_read(nfc, reg) & (~mask)) | val << shift);
}
-static inline void vf610_nfc_memcpy(void *dst, const void __iomem *src,
- size_t n)
+static inline bool vf610_nfc_kernel_is_little_endian(void)
{
- /*
- * Use this accessor for the internal SRAM buffers. On the ARM
- * Freescale Vybrid SoC it's known that the driver can treat
- * the SRAM buffer as if it's memory. Other platform might need
- * to treat the buffers differently.
- *
- * For the time being, use memcpy
- */
- memcpy(dst, src, n);
+#ifdef __LITTLE_ENDIAN
+ return true;
+#else
+ return false;
+#endif
+}
+
+/**
+ * Read accessor for internal SRAM buffer
+ * @dst: destination address in regular memory
+ * @src: source address in SRAM buffer
+ * @len: bytes to copy
+ * @fix_endian: Fix endianness if required
+ *
+ * Use this accessor for the internal SRAM buffers. On the ARM
+ * Freescale Vybrid SoC it's known that the driver can treat
+ * the SRAM buffer as if it's memory. Other platform might need
+ * to treat the buffers differently.
+ *
+ * The controller stores bytes from the NAND chip internally in big
+ * endianness. On little endian platforms such as Vybrid this leads
+ * to reversed byte order.
+ * For performance reason (and earlier probably due to unawareness)
+ * the driver avoids correcting endianness where it has control over
+ * write and read side (e.g. page wise data access).
+ */
+static inline void vf610_nfc_rd_from_sram(void *dst, const void __iomem *src,
+ size_t len, bool fix_endian)
+{
+ if (vf610_nfc_kernel_is_little_endian() && fix_endian) {
+ unsigned int i;
+
+ for (i = 0; i < len; i += 4) {
+ u32 val = swab32(__raw_readl(src + i));
+
+ memcpy(dst + i, &val, min(sizeof(val), len - i));
+ }
+ } else {
+ memcpy_fromio(dst, src, len);
+ }
+}
+
+/**
+ * Write accessor for internal SRAM buffer
+ * @dst: destination address in SRAM buffer
+ * @src: source address in regular memory
+ * @len: bytes to copy
+ * @fix_endian: Fix endianness if required
+ *
+ * Use this accessor for the internal SRAM buffers. On the ARM
+ * Freescale Vybrid SoC it's known that the driver can treat
+ * the SRAM buffer as if it's memory. Other platform might need
+ * to treat the buffers differently.
+ *
+ * The controller stores bytes from the NAND chip internally in big
+ * endianness. On little endian platforms such as Vybrid this leads
+ * to reversed byte order.
+ * For performance reason (and earlier probably due to unawareness)
+ * the driver avoids correcting endianness where it has control over
+ * write and read side (e.g. page wise data access).
+ */
+static inline void vf610_nfc_wr_to_sram(void __iomem *dst, const void *src,
+ size_t len, bool fix_endian)
+{
+ if (vf610_nfc_kernel_is_little_endian() && fix_endian) {
+ unsigned int i;
+
+ for (i = 0; i < len; i += 4) {
+ u32 val;
+
+ memcpy(&val, src + i, min(sizeof(val), len - i));
+ __raw_writel(swab32(val), dst + i);
+ }
+ } else {
+ memcpy_toio(dst, src, len);
+ }
}
/* Clear flags for upcoming command */
@@ -243,250 +314,185 @@ static void vf610_nfc_done(struct vf610_nfc *nfc)
vf610_nfc_clear_status(nfc);
}
-static u8 vf610_nfc_get_id(struct vf610_nfc *nfc, int col)
+static irqreturn_t vf610_nfc_irq(int irq, void *data)
{
- u32 flash_id;
+ struct mtd_info *mtd = data;
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- if (col < 4) {
- flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS1);
- flash_id >>= (3 - col) * 8;
- } else {
- flash_id = vf610_nfc_read(nfc, NFC_FLASH_STATUS2);
- flash_id >>= 24;
- }
+ vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
+ complete(&nfc->cmd_done);
- return flash_id & 0xff;
+ return IRQ_HANDLED;
}
-static u8 vf610_nfc_get_status(struct vf610_nfc *nfc)
+static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode)
{
- return vf610_nfc_read(nfc, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK;
+ vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG,
+ CONFIG_ECC_MODE_MASK,
+ CONFIG_ECC_MODE_SHIFT, ecc_mode);
}
-static void vf610_nfc_send_command(struct vf610_nfc *nfc, u32 cmd_byte1,
- u32 cmd_code)
+static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size)
{
- u32 tmp;
-
- vf610_nfc_clear_status(nfc);
-
- tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD2);
- tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK);
- tmp |= cmd_byte1 << CMD_BYTE1_SHIFT;
- tmp |= cmd_code << CMD_CODE_SHIFT;
- vf610_nfc_write(nfc, NFC_FLASH_CMD2, tmp);
+ vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size);
}
-static void vf610_nfc_send_commands(struct vf610_nfc *nfc, u32 cmd_byte1,
- u32 cmd_byte2, u32 cmd_code)
+static inline void vf610_nfc_run(struct vf610_nfc *nfc, u32 col, u32 row,
+ u32 cmd1, u32 cmd2, u32 trfr_sz)
{
- u32 tmp;
+ vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK,
+ COL_ADDR_SHIFT, col);
+
+ vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK,
+ ROW_ADDR_SHIFT, row);
+
+ vf610_nfc_write(nfc, NFC_SECTOR_SIZE, trfr_sz);
+ vf610_nfc_write(nfc, NFC_FLASH_CMD1, cmd1);
+ vf610_nfc_write(nfc, NFC_FLASH_CMD2, cmd2);
- vf610_nfc_send_command(nfc, cmd_byte1, cmd_code);
+ dev_dbg(nfc->dev,
+ "col 0x%04x, row 0x%08x, cmd1 0x%08x, cmd2 0x%08x, len %d\n",
+ col, row, cmd1, cmd2, trfr_sz);
- tmp = vf610_nfc_read(nfc, NFC_FLASH_CMD1);
- tmp &= ~CMD_BYTE2_MASK;
- tmp |= cmd_byte2 << CMD_BYTE2_SHIFT;
- vf610_nfc_write(nfc, NFC_FLASH_CMD1, tmp);
+ vf610_nfc_done(nfc);
}
-static irqreturn_t vf610_nfc_irq(int irq, void *data)
+static inline const struct nand_op_instr *
+vf610_get_next_instr(const struct nand_subop *subop, int *op_id)
{
- struct mtd_info *mtd = data;
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ if (*op_id + 1 >= subop->ninstrs)
+ return NULL;
- vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
- complete(&nfc->cmd_done);
+ (*op_id)++;
- return IRQ_HANDLED;
+ return &subop->instrs[*op_id];
}
-static void vf610_nfc_addr_cycle(struct vf610_nfc *nfc, int column, int page)
+static int vf610_nfc_cmd(struct nand_chip *chip,
+ const struct nand_subop *subop)
{
- if (column != -1) {
- if (nfc->chip.options & NAND_BUSWIDTH_16)
- column = column / 2;
- vf610_nfc_set_field(nfc, NFC_COL_ADDR, COL_ADDR_MASK,
- COL_ADDR_SHIFT, column);
+ const struct nand_op_instr *instr;
+ struct vf610_nfc *nfc = chip_to_nfc(chip);
+ int op_id = -1, trfr_sz = 0, offset;
+ u32 col = 0, row = 0, cmd1 = 0, cmd2 = 0, code = 0;
+ bool force8bit = false;
+
+ /*
+ * Some ops are optional, but the hardware requires the operations
+ * to be in this exact order.
+ * The op parser enforces the order and makes sure that there isn't
+ * a read and write element in a single operation.
+ */
+ instr = vf610_get_next_instr(subop, &op_id);
+ if (!instr)
+ return -EINVAL;
+
+ if (instr && instr->type == NAND_OP_CMD_INSTR) {
+ cmd2 |= instr->ctx.cmd.opcode << CMD_BYTE1_SHIFT;
+ code |= COMMAND_CMD_BYTE1;
+
+ instr = vf610_get_next_instr(subop, &op_id);
}
- if (page != -1)
- vf610_nfc_set_field(nfc, NFC_ROW_ADDR, ROW_ADDR_MASK,
- ROW_ADDR_SHIFT, page);
-}
-static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode)
-{
- vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG,
- CONFIG_ECC_MODE_MASK,
- CONFIG_ECC_MODE_SHIFT, ecc_mode);
-}
+ if (instr && instr->type == NAND_OP_ADDR_INSTR) {
+ int naddrs = nand_subop_get_num_addr_cyc(subop, op_id);
+ int i = nand_subop_get_addr_start_off(subop, op_id);
-static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size)
-{
- vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size);
-}
+ for (; i < naddrs; i++) {
+ u8 val = instr->ctx.addr.addrs[i];
-static void vf610_nfc_command(struct mtd_info *mtd, unsigned command,
- int column, int page)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0;
+ if (i < 2)
+ col |= COL_ADDR(i, val);
+ else
+ row |= ROW_ADDR(i - 2, val);
+ }
+ code |= COMMAND_NADDR_BYTES(naddrs);
- nfc->buf_offset = max(column, 0);
- nfc->alt_buf = ALT_BUF_DATA;
+ instr = vf610_get_next_instr(subop, &op_id);
+ }
- switch (command) {
- case NAND_CMD_SEQIN:
- /* Use valid column/page from preread... */
- vf610_nfc_addr_cycle(nfc, column, page);
- nfc->buf_offset = 0;
+ if (instr && instr->type == NAND_OP_DATA_OUT_INSTR) {
+ trfr_sz = nand_subop_get_data_len(subop, op_id);
+ offset = nand_subop_get_data_start_off(subop, op_id);
+ force8bit = instr->ctx.data.force_8bit;
/*
- * SEQIN => data => PAGEPROG sequence is done by the controller
- * hence we do not need to issue the command here...
+ * Don't fix endianness on page access for historical reasons.
+ * See comment in vf610_nfc_wr_to_sram
*/
- return;
- case NAND_CMD_PAGEPROG:
- trfr_sz += nfc->write_sz;
- vf610_nfc_transfer_size(nfc, trfr_sz);
- vf610_nfc_send_commands(nfc, NAND_CMD_SEQIN,
- command, PROGRAM_PAGE_CMD_CODE);
- if (nfc->use_hw_ecc)
- vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
- else
- vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
- break;
-
- case NAND_CMD_RESET:
- vf610_nfc_transfer_size(nfc, 0);
- vf610_nfc_send_command(nfc, command, RESET_CMD_CODE);
- break;
-
- case NAND_CMD_READOOB:
- trfr_sz += mtd->oobsize;
- column = mtd->writesize;
- vf610_nfc_transfer_size(nfc, trfr_sz);
- vf610_nfc_send_commands(nfc, NAND_CMD_READ0,
- NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, column, page);
- vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
- break;
-
- case NAND_CMD_READ0:
- trfr_sz += mtd->writesize + mtd->oobsize;
- vf610_nfc_transfer_size(nfc, trfr_sz);
- vf610_nfc_send_commands(nfc, NAND_CMD_READ0,
- NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, column, page);
- vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
- break;
-
- case NAND_CMD_PARAM:
- nfc->alt_buf = ALT_BUF_ONFI;
- trfr_sz = 3 * sizeof(struct nand_onfi_params);
- vf610_nfc_transfer_size(nfc, trfr_sz);
- vf610_nfc_send_command(nfc, command, READ_ONFI_PARAM_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, -1, column);
- vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
- break;
-
- case NAND_CMD_ERASE1:
- vf610_nfc_transfer_size(nfc, 0);
- vf610_nfc_send_commands(nfc, command,
- NAND_CMD_ERASE2, ERASE_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, column, page);
- break;
-
- case NAND_CMD_READID:
- nfc->alt_buf = ALT_BUF_ID;
- nfc->buf_offset = 0;
- vf610_nfc_transfer_size(nfc, 0);
- vf610_nfc_send_command(nfc, command, READ_ID_CMD_CODE);
- vf610_nfc_addr_cycle(nfc, -1, column);
- break;
-
- case NAND_CMD_STATUS:
- nfc->alt_buf = ALT_BUF_STAT;
- vf610_nfc_transfer_size(nfc, 0);
- vf610_nfc_send_command(nfc, command, STATUS_READ_CMD_CODE);
- break;
- default:
- return;
+ vf610_nfc_wr_to_sram(nfc->regs + NFC_MAIN_AREA(0) + offset,
+ instr->ctx.data.buf.out + offset,
+ trfr_sz, !nfc->data_access);
+ code |= COMMAND_WRITE_DATA;
+
+ instr = vf610_get_next_instr(subop, &op_id);
}
- vf610_nfc_done(nfc);
+ if (instr && instr->type == NAND_OP_CMD_INSTR) {
+ cmd1 |= instr->ctx.cmd.opcode << CMD_BYTE2_SHIFT;
+ code |= COMMAND_CMD_BYTE2;
- nfc->use_hw_ecc = false;
- nfc->write_sz = 0;
-}
+ instr = vf610_get_next_instr(subop, &op_id);
+ }
-static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- uint c = nfc->buf_offset;
+ if (instr && instr->type == NAND_OP_WAITRDY_INSTR) {
+ code |= COMMAND_RB_HANDSHAKE;
- /* Alternate buffers are only supported through read_byte */
- WARN_ON(nfc->alt_buf);
+ instr = vf610_get_next_instr(subop, &op_id);
+ }
- vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len);
+ if (instr && instr->type == NAND_OP_DATA_IN_INSTR) {
+ trfr_sz = nand_subop_get_data_len(subop, op_id);
+ offset = nand_subop_get_data_start_off(subop, op_id);
+ force8bit = instr->ctx.data.force_8bit;
- nfc->buf_offset += len;
-}
+ code |= COMMAND_READ_DATA;
+ }
-static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
- int len)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- uint c = nfc->buf_offset;
- uint l;
+ if (force8bit && (chip->options & NAND_BUSWIDTH_16))
+ vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
- l = min_t(uint, len, mtd->writesize + mtd->oobsize - c);
- vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l);
+ cmd2 |= code << CMD_CODE_SHIFT;
- nfc->write_sz += l;
- nfc->buf_offset += l;
-}
+ vf610_nfc_run(nfc, col, row, cmd1, cmd2, trfr_sz);
-static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd)
-{
- struct vf610_nfc *nfc = mtd_to_nfc(mtd);
- u8 tmp;
- uint c = nfc->buf_offset;
-
- switch (nfc->alt_buf) {
- case ALT_BUF_ID:
- tmp = vf610_nfc_get_id(nfc, c);
- break;
- case ALT_BUF_STAT:
- tmp = vf610_nfc_get_status(nfc);
- break;
-#ifdef __LITTLE_ENDIAN
- case ALT_BUF_ONFI:
- /* Reverse byte since the controller uses big endianness */
- c = nfc->buf_offset ^ 0x3;
- /* fall-through */
-#endif
- default:
- tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c));
- break;
+ if (instr && instr->type == NAND_OP_DATA_IN_INSTR) {
+ /*
+ * Don't fix endianness on page access for historical reasons.
+ * See comment in vf610_nfc_rd_from_sram
+ */
+ vf610_nfc_rd_from_sram(instr->ctx.data.buf.in + offset,
+ nfc->regs + NFC_MAIN_AREA(0) + offset,
+ trfr_sz, !nfc->data_access);
}
- nfc->buf_offset++;
- return tmp;
-}
-static u16 vf610_nfc_read_word(struct mtd_info *mtd)
-{
- u16 tmp;
+ if (force8bit && (chip->options & NAND_BUSWIDTH_16))
+ vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT);
- vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp));
- return tmp;
+ return 0;
}
-/* If not provided, upper layers apply a fixed delay. */
-static int vf610_nfc_dev_ready(struct mtd_info *mtd)
+static const struct nand_op_parser vf610_nfc_op_parser = NAND_OP_PARSER(
+ NAND_OP_PARSER_PATTERN(vf610_nfc_cmd,
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5),
+ NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, PAGE_2K + OOB_MAX),
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
+ NAND_OP_PARSER_PATTERN(vf610_nfc_cmd,
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5),
+ NAND_OP_PARSER_PAT_CMD_ELEM(true),
+ NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
+ NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, PAGE_2K + OOB_MAX)),
+ );
+
+static int vf610_nfc_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op,
+ bool check_only)
{
- /* NFC handles R/B internally; always ready. */
- return 1;
+ return nand_op_parser_exec_op(chip, &vf610_nfc_op_parser, op,
+ check_only);
}
/*
@@ -511,21 +517,6 @@ static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip)
vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp);
}
-/* Count the number of 0's in buff up to max_bits */
-static inline int count_written_bits(uint8_t *buff, int size, int max_bits)
-{
- uint32_t *buff32 = (uint32_t *)buff;
- int k, written_bits = 0;
-
- for (k = 0; k < (size / 4); k++) {
- written_bits += hweight32(~buff32[k]);
- if (unlikely(written_bits > max_bits))
- break;
- }
-
- return written_bits;
-}
-
static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat,
uint8_t *oob, int page)
{
@@ -541,9 +532,9 @@ static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat,
if (!(ecc_status & ECC_STATUS_MASK))
return ecc_count;
- /* Read OOB without ECC unit enabled */
- vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page);
- vf610_nfc_read_buf(mtd, oob, mtd->oobsize);
+ nfc->data_access = true;
+ nand_read_oob_op(&nfc->chip, page, 0, oob, mtd->oobsize);
+ nfc->data_access = false;
/*
* On an erased page, bit count (including OOB) should be zero or
@@ -554,15 +545,51 @@ static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat,
flips_threshold);
}
+static void vf610_nfc_fill_row(struct nand_chip *chip, int page, u32 *code,
+ u32 *row)
+{
+ *row = ROW_ADDR(0, page & 0xff) | ROW_ADDR(1, page >> 8);
+ *code |= COMMAND_RAR_BYTE1 | COMMAND_RAR_BYTE2;
+
+ if (chip->options & NAND_ROW_ADDR_3) {
+ *row |= ROW_ADDR(2, page >> 16);
+ *code |= COMMAND_RAR_BYTE3;
+ }
+}
+
static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- int eccsize = chip->ecc.size;
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int trfr_sz = mtd->writesize + mtd->oobsize;
+ u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
int stat;
- nand_read_page_op(chip, page, 0, buf, eccsize);
+ cmd2 |= NAND_CMD_READ0 << CMD_BYTE1_SHIFT;
+ code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2;
+
+ vf610_nfc_fill_row(chip, page, &code, &row);
+
+ cmd1 |= NAND_CMD_READSTART << CMD_BYTE2_SHIFT;
+ code |= COMMAND_CMD_BYTE2 | COMMAND_RB_HANDSHAKE | COMMAND_READ_DATA;
+
+ cmd2 |= code << CMD_CODE_SHIFT;
+
+ vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
+ vf610_nfc_run(nfc, 0, row, cmd1, cmd2, trfr_sz);
+ vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
+
+ /*
+ * Don't fix endianness on page access for historical reasons.
+ * See comment in vf610_nfc_rd_from_sram
+ */
+ vf610_nfc_rd_from_sram(buf, nfc->regs + NFC_MAIN_AREA(0),
+ mtd->writesize, false);
if (oob_required)
- vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ vf610_nfc_rd_from_sram(chip->oob_poi,
+ nfc->regs + NFC_MAIN_AREA(0) +
+ mtd->writesize,
+ mtd->oobsize, false);
stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page);
@@ -579,14 +606,103 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page)
{
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int trfr_sz = mtd->writesize + mtd->oobsize;
+ u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
+ u8 status;
+ int ret;
- nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
- if (oob_required)
- vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ cmd2 |= NAND_CMD_SEQIN << CMD_BYTE1_SHIFT;
+ code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2;
+
+ vf610_nfc_fill_row(chip, page, &code, &row);
+
+ cmd1 |= NAND_CMD_PAGEPROG << CMD_BYTE2_SHIFT;
+ code |= COMMAND_CMD_BYTE2 | COMMAND_WRITE_DATA;
+
+ /*
+ * Don't fix endianness on page access for historical reasons.
+ * See comment in vf610_nfc_wr_to_sram
+ */
+ vf610_nfc_wr_to_sram(nfc->regs + NFC_MAIN_AREA(0), buf,
+ mtd->writesize, false);
+
+ code |= COMMAND_RB_HANDSHAKE;
+ cmd2 |= code << CMD_CODE_SHIFT;
+
+ vf610_nfc_ecc_mode(nfc, nfc->ecc_mode);
+ vf610_nfc_run(nfc, 0, row, cmd1, cmd2, trfr_sz);
+ vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
+
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+
+static int vf610_nfc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf,
+ int oob_required, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int ret;
+
+ nfc->data_access = true;
+ ret = nand_read_page_raw(mtd, chip, buf, oob_required, page);
+ nfc->data_access = false;
+
+ return ret;
+}
+
+static int vf610_nfc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int ret;
+
+ nfc->data_access = true;
+ ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
+ if (!ret && oob_required)
+ ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize,
+ false);
+ nfc->data_access = false;
- /* Always write whole page including OOB due to HW ECC */
- nfc->use_hw_ecc = true;
- nfc->write_sz = mtd->writesize + mtd->oobsize;
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int vf610_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int ret;
+
+ nfc->data_access = true;
+ ret = nand_read_oob_std(mtd, chip, page);
+ nfc->data_access = false;
+
+ return ret;
+}
+
+static int vf610_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int ret;
+
+ nfc->data_access = true;
+ ret = nand_prog_page_begin_op(chip, page, mtd->writesize,
+ chip->oob_poi, mtd->oobsize);
+ nfc->data_access = false;
+
+ if (ret)
+ return ret;
return nand_prog_page_end_op(chip);
}
@@ -605,6 +721,7 @@ static void vf610_nfc_preinit_controller(struct vf610_nfc *nfc)
vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT);
vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT);
vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT);
+ vf610_nfc_ecc_mode(nfc, ECC_BYPASS);
/* Disable virtual pages, only one elementary transfer unit */
vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK,
@@ -682,7 +799,7 @@ static int vf610_nfc_probe(struct platform_device *pdev)
dev_err(nfc->dev,
"Only one NAND chip supported!\n");
err = -EINVAL;
- goto error;
+ goto err_disable_clk;
}
nand_set_flash_node(chip, child);
@@ -692,18 +809,11 @@ static int vf610_nfc_probe(struct platform_device *pdev)
if (!nand_get_flash_node(chip)) {
dev_err(nfc->dev, "NAND chip sub-node missing!\n");
err = -ENODEV;
- goto err_clk;
+ goto err_disable_clk;
}
- chip->dev_ready = vf610_nfc_dev_ready;
- chip->cmdfunc = vf610_nfc_command;
- chip->read_byte = vf610_nfc_read_byte;
- chip->read_word = vf610_nfc_read_word;
- chip->read_buf = vf610_nfc_read_buf;
- chip->write_buf = vf610_nfc_write_buf;
+ chip->exec_op = vf610_nfc_exec_op;
chip->select_chip = vf610_nfc_select_chip;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
chip->options |= NAND_NO_SUBPAGE_WRITE;
@@ -712,7 +822,7 @@ static int vf610_nfc_probe(struct platform_device *pdev)
err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd);
if (err) {
dev_err(nfc->dev, "Error requesting IRQ!\n");
- goto error;
+ goto err_disable_clk;
}
vf610_nfc_preinit_controller(nfc);
@@ -720,7 +830,7 @@ static int vf610_nfc_probe(struct platform_device *pdev)
/* first scan to find the device and get the page size */
err = nand_scan_ident(mtd, 1, NULL);
if (err)
- goto error;
+ goto err_disable_clk;
vf610_nfc_init_controller(nfc);
@@ -732,20 +842,20 @@ static int vf610_nfc_probe(struct platform_device *pdev)
if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) {
dev_err(nfc->dev, "Unsupported flash page size\n");
err = -ENXIO;
- goto error;
+ goto err_disable_clk;
}
if (chip->ecc.mode == NAND_ECC_HW) {
if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) {
dev_err(nfc->dev, "Unsupported flash with hwecc\n");
err = -ENXIO;
- goto error;
+ goto err_disable_clk;
}
if (chip->ecc.size != mtd->writesize) {
dev_err(nfc->dev, "Step size needs to be page size\n");
err = -ENXIO;
- goto error;
+ goto err_disable_clk;
}
/* Only 64 byte ECC layouts known */
@@ -763,11 +873,15 @@ static int vf610_nfc_probe(struct platform_device *pdev)
} else {
dev_err(nfc->dev, "Unsupported ECC strength\n");
err = -ENXIO;
- goto error;
+ goto err_disable_clk;
}
chip->ecc.read_page = vf610_nfc_read_page;
chip->ecc.write_page = vf610_nfc_write_page;
+ chip->ecc.read_page_raw = vf610_nfc_read_page_raw;
+ chip->ecc.write_page_raw = vf610_nfc_write_page_raw;
+ chip->ecc.read_oob = vf610_nfc_read_oob;
+ chip->ecc.write_oob = vf610_nfc_write_oob;
chip->ecc.size = PAGE_2K;
}
@@ -775,16 +889,19 @@ static int vf610_nfc_probe(struct platform_device *pdev)
/* second phase scan */
err = nand_scan_tail(mtd);
if (err)
- goto error;
+ goto err_disable_clk;
platform_set_drvdata(pdev, mtd);
/* Register device in MTD */
- return mtd_device_register(mtd, NULL, 0);
+ err = mtd_device_register(mtd, NULL, 0);
+ if (err)
+ goto err_cleanup_nand;
+ return 0;
-error:
- of_node_put(nand_get_flash_node(chip));
-err_clk:
+err_cleanup_nand:
+ nand_cleanup(chip);
+err_disable_clk:
clk_disable_unprepare(nfc->clk);
return err;
}
diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c
index 9926b4e3d69d..9926b4e3d69d 100644
--- a/drivers/mtd/nand/xway_nand.c
+++ b/drivers/mtd/nand/raw/xway_nand.c
diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c
index 184c8fbfe465..a6fbfa4e5799 100644
--- a/drivers/mtd/nftlmount.c
+++ b/drivers/mtd/nftlmount.c
@@ -122,8 +122,7 @@ static int find_boot_record(struct NFTLrecord *nftl)
if (memcmp(buf, "ANAND", 6)) {
printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
block * nftl->EraseSize, nftl->mbd.mtd->index);
- printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+ printk(KERN_NOTICE "New data are: %6ph\n", buf);
continue;
}
#endif
@@ -328,12 +327,9 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
memset(instr, 0, sizeof(struct erase_info));
/* XXX: use async erase interface, XXX: test return code */
- instr->mtd = nftl->mbd.mtd;
instr->addr = block * nftl->EraseSize;
instr->len = nftl->EraseSize;
- mtd_erase(mtd, instr);
-
- if (instr->state == MTD_ERASE_FAILED) {
+ if (mtd_erase(mtd, instr)) {
printk("Error while formatting block %d\n", block);
goto fail;
}
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
index 6bdf4e525677..615f8c173162 100644
--- a/drivers/mtd/ofpart.c
+++ b/drivers/mtd/ofpart.c
@@ -25,9 +25,9 @@ static bool node_has_compatible(struct device_node *pp)
return of_get_property(pp, "compatible", NULL);
}
-static int parse_ofpart_partitions(struct mtd_info *master,
- const struct mtd_partition **pparts,
- struct mtd_part_parser_data *data)
+static int parse_fixed_partitions(struct mtd_info *master,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
struct device_node *mtd_node;
@@ -140,9 +140,16 @@ ofpart_none:
return ret;
}
+static const struct of_device_id parse_ofpart_match_table[] = {
+ { .compatible = "fixed-partitions" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, parse_ofpart_match_table);
+
static struct mtd_part_parser ofpart_parser = {
- .parse_fn = parse_ofpart_partitions,
- .name = "ofpart",
+ .parse_fn = parse_fixed_partitions,
+ .name = "fixed-partitions",
+ .of_match_table = parse_ofpart_match_table,
};
static int parse_ofoldpart_partitions(struct mtd_info *master,
@@ -229,4 +236,5 @@ MODULE_AUTHOR("Vitaly Wool, David Gibson");
* with the same name. Since we provide the ofoldpart parser, we should have
* the corresponding alias.
*/
+MODULE_ALIAS("fixed-partitions");
MODULE_ALIAS("ofoldpart");
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
index d1cbf26db2c0..df27f24ce0fa 100644
--- a/drivers/mtd/rfd_ftl.c
+++ b/drivers/mtd/rfd_ftl.c
@@ -266,91 +266,54 @@ static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *b
return 0;
}
-static void erase_callback(struct erase_info *erase)
-{
- struct partition *part;
- u16 magic;
- int i, rc;
- size_t retlen;
-
- part = (struct partition*)erase->priv;
-
- i = (u32)erase->addr / part->block_size;
- if (i >= part->total_blocks || part->blocks[i].offset != erase->addr ||
- erase->addr > UINT_MAX) {
- printk(KERN_ERR PREFIX "erase callback for unknown offset %llx "
- "on '%s'\n", (unsigned long long)erase->addr, part->mbd.mtd->name);
- return;
- }
-
- if (erase->state != MTD_ERASE_DONE) {
- printk(KERN_WARNING PREFIX "erase failed at 0x%llx on '%s', "
- "state %d\n", (unsigned long long)erase->addr,
- part->mbd.mtd->name, erase->state);
-
- part->blocks[i].state = BLOCK_FAILED;
- part->blocks[i].free_sectors = 0;
- part->blocks[i].used_sectors = 0;
-
- kfree(erase);
-
- return;
- }
-
- magic = cpu_to_le16(RFD_MAGIC);
-
- part->blocks[i].state = BLOCK_ERASED;
- part->blocks[i].free_sectors = part->data_sectors_per_block;
- part->blocks[i].used_sectors = 0;
- part->blocks[i].erases++;
-
- rc = mtd_write(part->mbd.mtd, part->blocks[i].offset, sizeof(magic),
- &retlen, (u_char *)&magic);
-
- if (!rc && retlen != sizeof(magic))
- rc = -EIO;
-
- if (rc) {
- printk(KERN_ERR PREFIX "'%s': unable to write RFD "
- "header at 0x%lx\n",
- part->mbd.mtd->name,
- part->blocks[i].offset);
- part->blocks[i].state = BLOCK_FAILED;
- }
- else
- part->blocks[i].state = BLOCK_OK;
-
- kfree(erase);
-}
-
static int erase_block(struct partition *part, int block)
{
struct erase_info *erase;
- int rc = -ENOMEM;
+ int rc;
erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
if (!erase)
- goto err;
+ return -ENOMEM;
- erase->mtd = part->mbd.mtd;
- erase->callback = erase_callback;
erase->addr = part->blocks[block].offset;
erase->len = part->block_size;
- erase->priv = (u_long)part;
part->blocks[block].state = BLOCK_ERASING;
part->blocks[block].free_sectors = 0;
rc = mtd_erase(part->mbd.mtd, erase);
-
if (rc) {
printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' "
"failed\n", (unsigned long long)erase->addr,
(unsigned long long)erase->len, part->mbd.mtd->name);
- kfree(erase);
+ part->blocks[block].state = BLOCK_FAILED;
+ part->blocks[block].free_sectors = 0;
+ part->blocks[block].used_sectors = 0;
+ } else {
+ u16 magic = cpu_to_le16(RFD_MAGIC);
+ size_t retlen;
+
+ part->blocks[block].state = BLOCK_ERASED;
+ part->blocks[block].free_sectors = part->data_sectors_per_block;
+ part->blocks[block].used_sectors = 0;
+ part->blocks[block].erases++;
+
+ rc = mtd_write(part->mbd.mtd, part->blocks[block].offset,
+ sizeof(magic), &retlen, (u_char *)&magic);
+ if (!rc && retlen != sizeof(magic))
+ rc = -EIO;
+
+ if (rc) {
+ pr_err(PREFIX "'%s': unable to write RFD header at 0x%lx\n",
+ part->mbd.mtd->name, part->blocks[block].offset);
+ part->blocks[block].state = BLOCK_FAILED;
+ } else {
+ part->blocks[block].state = BLOCK_OK;
+ }
}
-err:
+ kfree(erase);
+
return rc;
}
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index 4237c7cebf02..79636349df96 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -17,7 +17,7 @@
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/mtd/nand_ecc.h>
-#include "nand/sm_common.h"
+#include "nand/raw/sm_common.h"
#include "sm_ftl.h"
@@ -460,11 +460,8 @@ static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block,
struct mtd_info *mtd = ftl->trans->mtd;
struct erase_info erase;
- erase.mtd = mtd;
- erase.callback = sm_erase_callback;
erase.addr = sm_mkoffset(ftl, zone_num, block, 0);
erase.len = ftl->block_size;
- erase.priv = (u_long)ftl;
if (ftl->unstable)
return -EIO;
@@ -482,15 +479,6 @@ static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block,
goto error;
}
- if (erase.state == MTD_ERASE_PENDING)
- wait_for_completion(&ftl->erase_completion);
-
- if (erase.state != MTD_ERASE_DONE) {
- sm_printk("erase of block %d in zone %d failed after wait",
- block, zone_num);
- goto error;
- }
-
if (put_free)
kfifo_in(&zone->free_sectors,
(const unsigned char *)&block, sizeof(block));
@@ -501,12 +489,6 @@ error:
return -EIO;
}
-static void sm_erase_callback(struct erase_info *self)
-{
- struct sm_ftl *ftl = (struct sm_ftl *)self->priv;
- complete(&ftl->erase_completion);
-}
-
/* Thoroughly test that block is valid. */
static int sm_check_block(struct sm_ftl *ftl, int zone, int block)
{
@@ -1141,7 +1123,6 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
mutex_init(&ftl->mutex);
timer_setup(&ftl->timer, sm_cache_flush_timer, 0);
INIT_WORK(&ftl->flush_work, sm_cache_flush_work);
- init_completion(&ftl->erase_completion);
/* Read media information */
if (sm_get_media_info(ftl, mtd)) {
diff --git a/drivers/mtd/sm_ftl.h b/drivers/mtd/sm_ftl.h
index 43bb7300785b..0a46d75cdc6a 100644
--- a/drivers/mtd/sm_ftl.h
+++ b/drivers/mtd/sm_ftl.h
@@ -53,9 +53,6 @@ struct sm_ftl {
struct work_struct flush_work;
struct timer_list timer;
- /* Async erase stuff */
- struct completion erase_completion;
-
/* Geometry stuff */
int heads;
int sectors;
@@ -86,7 +83,6 @@ struct chs_entry {
printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__)
-static void sm_erase_callback(struct erase_info *self);
static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block,
int put_free);
static void sm_mark_block_bad(struct sm_ftl *ftl, int zone_num, int block);
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 2901c7bd9e30..3e3c0bbc45c0 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -1051,6 +1051,24 @@ static int fsl_qspi_probe(struct platform_device *pdev)
spi_nor_set_flash_node(nor, np);
nor->priv = q;
+ if (q->nor_num > 1 && !mtd->name) {
+ int spiflash_idx;
+
+ ret = of_property_read_u32(np, "reg", &spiflash_idx);
+ if (!ret) {
+ mtd->name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s-%d",
+ dev_name(dev),
+ spiflash_idx);
+ if (!mtd->name) {
+ ret = -ENOMEM;
+ goto mutex_failed;
+ }
+ } else {
+ dev_warn(dev, "reg property is missing\n");
+ }
+ }
+
/* fill the hooks */
nor->read_reg = fsl_qspi_read_reg;
nor->write_reg = fsl_qspi_write_reg;
@@ -1174,7 +1192,6 @@ static int fsl_qspi_resume(struct platform_device *pdev)
static struct platform_driver fsl_qspi_driver = {
.driver = {
.name = "fsl-quadspi",
- .bus = &platform_bus_type,
.of_match_table = fsl_qspi_dt_ids,
},
.probe = fsl_qspi_probe,
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d445a4d3b770..5bfa36e95f35 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -560,9 +560,6 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
erase_err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
- instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return ret;
}
diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c
index 3d0b8b5c1a53..c84250beffdc 100644
--- a/drivers/mtd/tests/mtd_test.c
+++ b/drivers/mtd/tests/mtd_test.c
@@ -14,7 +14,6 @@ int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum)
loff_t addr = (loff_t)ebnum * mtd->erasesize;
memset(&ei, 0, sizeof(struct erase_info));
- ei.mtd = mtd;
ei.addr = addr;
ei.len = mtd->erasesize;
@@ -24,10 +23,6 @@ int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum)
return err;
}
- if (ei.state == MTD_ERASE_FAILED) {
- pr_info("some erase error occurred at EB %d\n", ebnum);
- return -EIO;
- }
return 0;
}
diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c
index ff1e0565b020..bc303cac9f43 100644
--- a/drivers/mtd/tests/pagetest.c
+++ b/drivers/mtd/tests/pagetest.c
@@ -435,9 +435,13 @@ static int __init mtd_pagetest_init(void)
if (err)
goto out;
- err = erasecrosstest();
- if (err)
- goto out;
+ if (ebcnt > 1) {
+ err = erasecrosstest();
+ if (err)
+ goto out;
+ } else {
+ pr_info("skipping erasecrosstest, 2 erase blocks needed\n");
+ }
err = erasetest();
if (err)
diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c
index 0b89418a0888..20edb3b49c77 100644
--- a/drivers/mtd/tests/speedtest.c
+++ b/drivers/mtd/tests/speedtest.c
@@ -59,7 +59,6 @@ static int multiblock_erase(int ebnum, int blocks)
loff_t addr = (loff_t)ebnum * mtd->erasesize;
memset(&ei, 0, sizeof(struct erase_info));
- ei.mtd = mtd;
ei.addr = addr;
ei.len = mtd->erasesize * blocks;
@@ -70,12 +69,6 @@ static int multiblock_erase(int ebnum, int blocks)
return err;
}
- if (ei.state == MTD_ERASE_FAILED) {
- pr_err("some erase error occurred at EB %d,"
- "blocks %d\n", ebnum, blocks);
- return -EIO;
- }
-
return 0;
}
diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c
index 1cb287ec32ad..6b655a53113b 100644
--- a/drivers/mtd/ubi/gluebi.c
+++ b/drivers/mtd/ubi/gluebi.c
@@ -272,12 +272,9 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
if (err)
goto out_err;
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
return 0;
out_err:
- instr->state = MTD_ERASE_FAILED;
instr->fail_addr = (long long)lnum * mtd->erasesize;
return err;
}
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 8290432017ce..0e3a76a9e2f8 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -309,18 +309,6 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
}
/**
- * erase_callback - MTD erasure call-back.
- * @ei: MTD erase information object.
- *
- * Note, even though MTD erase interface is asynchronous, all the current
- * implementations are synchronous anyway.
- */
-static void erase_callback(struct erase_info *ei)
-{
- wake_up_interruptible((wait_queue_head_t *)ei->priv);
-}
-
-/**
* do_sync_erase - synchronously erase a physical eraseblock.
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to erase
@@ -333,7 +321,6 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum)
{
int err, retries = 0;
struct erase_info ei;
- wait_queue_head_t wq;
dbg_io("erase PEB %d", pnum);
ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
@@ -344,14 +331,10 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum)
}
retry:
- init_waitqueue_head(&wq);
memset(&ei, 0, sizeof(struct erase_info));
- ei.mtd = ubi->mtd;
ei.addr = (loff_t)pnum * ubi->peb_size;
ei.len = ubi->peb_size;
- ei.callback = erase_callback;
- ei.priv = (unsigned long)&wq;
err = mtd_erase(ubi->mtd, &ei);
if (err) {
@@ -366,25 +349,6 @@ retry:
return err;
}
- err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE ||
- ei.state == MTD_ERASE_FAILED);
- if (err) {
- ubi_err(ubi, "interrupted PEB %d erasure", pnum);
- return -EINTR;
- }
-
- if (ei.state == MTD_ERASE_FAILED) {
- if (retries++ < UBI_IO_RETRIES) {
- ubi_warn(ubi, "error while erasing PEB %d, retry",
- pnum);
- yield();
- goto retry;
- }
- ubi_err(ubi, "cannot erase PEB %d", pnum);
- dump_stack();
- return -EIO;
- }
-
err = ubi_self_check_all_ff(ubi, pnum, 0, ubi->peb_size);
if (err)
return err;
diff --git a/drivers/net/ethernet/sfc/falcon/mtd.c b/drivers/net/ethernet/sfc/falcon/mtd.c
index cde593cb1052..2d67e4621a3d 100644
--- a/drivers/net/ethernet/sfc/falcon/mtd.c
+++ b/drivers/net/ethernet/sfc/falcon/mtd.c
@@ -24,17 +24,8 @@
static int ef4_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
{
struct ef4_nic *efx = mtd->priv;
- int rc;
- rc = efx->type->mtd_erase(mtd, erase->addr, erase->len);
- if (rc == 0) {
- erase->state = MTD_ERASE_DONE;
- } else {
- erase->state = MTD_ERASE_FAILED;
- erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- }
- mtd_erase_callback(erase);
- return rc;
+ return efx->type->mtd_erase(mtd, erase->addr, erase->len);
}
static void ef4_mtd_sync(struct mtd_info *mtd)
diff --git a/drivers/net/ethernet/sfc/mtd.c b/drivers/net/ethernet/sfc/mtd.c
index a77a8bd2dd70..4ac30b6e5dab 100644
--- a/drivers/net/ethernet/sfc/mtd.c
+++ b/drivers/net/ethernet/sfc/mtd.c
@@ -24,17 +24,8 @@
static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
{
struct efx_nic *efx = mtd->priv;
- int rc;
- rc = efx->type->mtd_erase(mtd, erase->addr, erase->len);
- if (rc == 0) {
- erase->state = MTD_ERASE_DONE;
- } else {
- erase->state = MTD_ERASE_FAILED;
- erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- }
- mtd_erase_callback(erase);
- return rc;
+ return efx->type->mtd_erase(mtd, erase->addr, erase->len);
}
static void efx_mtd_sync(struct mtd_info *mtd)
diff --git a/drivers/staging/goldfish/goldfish_nand.c b/drivers/staging/goldfish/goldfish_nand.c
index 52cc1363993e..f5e002ecba75 100644
--- a/drivers/staging/goldfish/goldfish_nand.c
+++ b/drivers/staging/goldfish/goldfish_nand.c
@@ -119,9 +119,6 @@ static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
return -EIO;
}
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
-
return 0;
invalid_arg:
diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c
index d20284b49557..448478451c4c 100644
--- a/drivers/staging/mt29f_spinand/mt29f_spinand.c
+++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c
@@ -921,8 +921,8 @@ static int spinand_probe(struct spi_device *spi_nand)
chip->waitfunc = spinand_wait;
chip->options |= NAND_CACHEPRG;
chip->select_chip = spinand_select_chip;
- chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
- chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
+ chip->set_features = nand_get_set_features_notsupp;
+ chip->get_features = nand_get_set_features_notsupp;
mtd = nand_to_mtd(chip);
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
index 4a6cf289be24..83b8f06b4a64 100644
--- a/fs/jffs2/erase.c
+++ b/fs/jffs2/erase.c
@@ -21,14 +21,6 @@
#include <linux/pagemap.h>
#include "nodelist.h"
-struct erase_priv_struct {
- struct jffs2_eraseblock *jeb;
- struct jffs2_sb_info *c;
-};
-
-#ifndef __ECOS
-static void jffs2_erase_callback(struct erase_info *);
-#endif
static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
@@ -51,7 +43,7 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
jffs2_dbg(1, "%s(): erase block %#08x (range %#08x-%#08x)\n",
__func__,
jeb->offset, jeb->offset, jeb->offset + c->sector_size);
- instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
+ instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
if (!instr) {
pr_warn("kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
mutex_lock(&c->erase_free_sem);
@@ -67,18 +59,15 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
memset(instr, 0, sizeof(*instr));
- instr->mtd = c->mtd;
instr->addr = jeb->offset;
instr->len = c->sector_size;
- instr->callback = jffs2_erase_callback;
- instr->priv = (unsigned long)(&instr[1]);
-
- ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
- ((struct erase_priv_struct *)instr->priv)->c = c;
ret = mtd_erase(c->mtd, instr);
- if (!ret)
+ if (!ret) {
+ jffs2_erase_succeeded(c, jeb);
+ kfree(instr);
return;
+ }
bad_offset = instr->fail_addr;
kfree(instr);
@@ -214,22 +203,6 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock
wake_up(&c->erase_wait);
}
-#ifndef __ECOS
-static void jffs2_erase_callback(struct erase_info *instr)
-{
- struct erase_priv_struct *priv = (void *)instr->priv;
-
- if(instr->state != MTD_ERASE_DONE) {
- pr_warn("Erase at 0x%08llx finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n",
- (unsigned long long)instr->addr, instr->state);
- jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
- } else {
- jffs2_erase_succeeded(priv->c, priv->jeb);
- }
- kfree(instr);
-}
-#endif /* !__ECOS */
-
/* Hmmm. Maybe we should accept the extra space it takes and make
this a standard doubly-linked list? */
static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h
index 3bf8f954b642..3102bd754d18 100644
--- a/include/linux/mtd/bbm.h
+++ b/include/linux/mtd/bbm.h
@@ -1,6 +1,4 @@
/*
- * linux/include/linux/mtd/bbm.h
- *
* NAND family Bad Block Management (BBM) header file
* - Bad Block Table (BBT) implementation
*
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 205ededccc60..a86c4fa93115 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -30,32 +30,19 @@
#include <asm/div64.h>
-#define MTD_ERASE_PENDING 0x01
-#define MTD_ERASING 0x02
-#define MTD_ERASE_SUSPEND 0x04
-#define MTD_ERASE_DONE 0x08
-#define MTD_ERASE_FAILED 0x10
-
#define MTD_FAIL_ADDR_UNKNOWN -1LL
+struct mtd_info;
+
/*
* If the erase fails, fail_addr might indicate exactly which block failed. If
* fail_addr = MTD_FAIL_ADDR_UNKNOWN, the failure was not at the device level
* or was not specific to any particular block.
*/
struct erase_info {
- struct mtd_info *mtd;
uint64_t addr;
uint64_t len;
uint64_t fail_addr;
- u_long time;
- u_long retries;
- unsigned dev;
- unsigned cell;
- void (*callback) (struct erase_info *self);
- u_long priv;
- u_char state;
- struct erase_info *next;
};
struct mtd_erase_region_info {
@@ -595,8 +582,6 @@ extern void register_mtd_user (struct mtd_notifier *new);
extern int unregister_mtd_user (struct mtd_notifier *old);
void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size);
-void mtd_erase_callback(struct erase_info *instr);
-
static inline int mtd_is_bitflip(int err) {
return err == -EUCLEAN;
}
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
new file mode 100644
index 000000000000..792ea5c26329
--- /dev/null
+++ b/include/linux/mtd/nand.h
@@ -0,0 +1,731 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2017 - Free Electrons
+ *
+ * Authors:
+ * Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Peter Pan <peterpandong@micron.com>
+ */
+
+#ifndef __LINUX_MTD_NAND_H
+#define __LINUX_MTD_NAND_H
+
+#include <linux/mtd/mtd.h>
+
+/**
+ * struct nand_memory_organization - Memory organization structure
+ * @bits_per_cell: number of bits per NAND cell
+ * @pagesize: page size
+ * @oobsize: OOB area size
+ * @pages_per_eraseblock: number of pages per eraseblock
+ * @eraseblocks_per_lun: number of eraseblocks per LUN (Logical Unit Number)
+ * @planes_per_lun: number of planes per LUN
+ * @luns_per_target: number of LUN per target (target is a synonym for die)
+ * @ntargets: total number of targets exposed by the NAND device
+ */
+struct nand_memory_organization {
+ unsigned int bits_per_cell;
+ unsigned int pagesize;
+ unsigned int oobsize;
+ unsigned int pages_per_eraseblock;
+ unsigned int eraseblocks_per_lun;
+ unsigned int planes_per_lun;
+ unsigned int luns_per_target;
+ unsigned int ntargets;
+};
+
+#define NAND_MEMORG(bpc, ps, os, ppe, epl, ppl, lpt, nt) \
+ { \
+ .bits_per_cell = (bpc), \
+ .pagesize = (ps), \
+ .oobsize = (os), \
+ .pages_per_eraseblock = (ppe), \
+ .eraseblocks_per_lun = (epl), \
+ .planes_per_lun = (ppl), \
+ .luns_per_target = (lpt), \
+ .ntargets = (nt), \
+ }
+
+/**
+ * struct nand_row_converter - Information needed to convert an absolute offset
+ * into a row address
+ * @lun_addr_shift: position of the LUN identifier in the row address
+ * @eraseblock_addr_shift: position of the eraseblock identifier in the row
+ * address
+ */
+struct nand_row_converter {
+ unsigned int lun_addr_shift;
+ unsigned int eraseblock_addr_shift;
+};
+
+/**
+ * struct nand_pos - NAND position object
+ * @target: the NAND target/die
+ * @lun: the LUN identifier
+ * @plane: the plane within the LUN
+ * @eraseblock: the eraseblock within the LUN
+ * @page: the page within the LUN
+ *
+ * These information are usually used by specific sub-layers to select the
+ * appropriate target/die and generate a row address to pass to the device.
+ */
+struct nand_pos {
+ unsigned int target;
+ unsigned int lun;
+ unsigned int plane;
+ unsigned int eraseblock;
+ unsigned int page;
+};
+
+/**
+ * struct nand_page_io_req - NAND I/O request object
+ * @pos: the position this I/O request is targeting
+ * @dataoffs: the offset within the page
+ * @datalen: number of data bytes to read from/write to this page
+ * @databuf: buffer to store data in or get data from
+ * @ooboffs: the OOB offset within the page
+ * @ooblen: the number of OOB bytes to read from/write to this page
+ * @oobbuf: buffer to store OOB data in or get OOB data from
+ *
+ * This object is used to pass per-page I/O requests to NAND sub-layers. This
+ * way all useful information are already formatted in a useful way and
+ * specific NAND layers can focus on translating these information into
+ * specific commands/operations.
+ */
+struct nand_page_io_req {
+ struct nand_pos pos;
+ unsigned int dataoffs;
+ unsigned int datalen;
+ union {
+ const void *out;
+ void *in;
+ } databuf;
+ unsigned int ooboffs;
+ unsigned int ooblen;
+ union {
+ const void *out;
+ void *in;
+ } oobbuf;
+};
+
+/**
+ * struct nand_ecc_req - NAND ECC requirements
+ * @strength: ECC strength
+ * @step_size: ECC step/block size
+ */
+struct nand_ecc_req {
+ unsigned int strength;
+ unsigned int step_size;
+};
+
+#define NAND_ECCREQ(str, stp) { .strength = (str), .step_size = (stp) }
+
+/**
+ * struct nand_bbt - bad block table object
+ * @cache: in memory BBT cache
+ */
+struct nand_bbt {
+ unsigned long *cache;
+};
+
+struct nand_device;
+
+/**
+ * struct nand_ops - NAND operations
+ * @erase: erase a specific block. No need to check if the block is bad before
+ * erasing, this has been taken care of by the generic NAND layer
+ * @markbad: mark a specific block bad. No need to check if the block is
+ * already marked bad, this has been taken care of by the generic
+ * NAND layer. This method should just write the BBM (Bad Block
+ * Marker) so that future call to struct_nand_ops->isbad() return
+ * true
+ * @isbad: check whether a block is bad or not. This method should just read
+ * the BBM and return whether the block is bad or not based on what it
+ * reads
+ *
+ * These are all low level operations that should be implemented by specialized
+ * NAND layers (SPI NAND, raw NAND, ...).
+ */
+struct nand_ops {
+ int (*erase)(struct nand_device *nand, const struct nand_pos *pos);
+ int (*markbad)(struct nand_device *nand, const struct nand_pos *pos);
+ bool (*isbad)(struct nand_device *nand, const struct nand_pos *pos);
+};
+
+/**
+ * struct nand_device - NAND device
+ * @mtd: MTD instance attached to the NAND device
+ * @memorg: memory layout
+ * @eccreq: ECC requirements
+ * @rowconv: position to row address converter
+ * @bbt: bad block table info
+ * @ops: NAND operations attached to the NAND device
+ *
+ * Generic NAND object. Specialized NAND layers (raw NAND, SPI NAND, OneNAND)
+ * should declare their own NAND object embedding a nand_device struct (that's
+ * how inheritance is done).
+ * struct_nand_device->memorg and struct_nand_device->eccreq should be filled
+ * at device detection time to reflect the NAND device
+ * capabilities/requirements. Once this is done nanddev_init() can be called.
+ * It will take care of converting NAND information into MTD ones, which means
+ * the specialized NAND layers should never manually tweak
+ * struct_nand_device->mtd except for the ->_read/write() hooks.
+ */
+struct nand_device {
+ struct mtd_info mtd;
+ struct nand_memory_organization memorg;
+ struct nand_ecc_req eccreq;
+ struct nand_row_converter rowconv;
+ struct nand_bbt bbt;
+ const struct nand_ops *ops;
+};
+
+/**
+ * struct nand_io_iter - NAND I/O iterator
+ * @req: current I/O request
+ * @oobbytes_per_page: maximum number of OOB bytes per page
+ * @dataleft: remaining number of data bytes to read/write
+ * @oobleft: remaining number of OOB bytes to read/write
+ *
+ * Can be used by specialized NAND layers to iterate over all pages covered
+ * by an MTD I/O request, which should greatly simplifies the boiler-plate
+ * code needed to read/write data from/to a NAND device.
+ */
+struct nand_io_iter {
+ struct nand_page_io_req req;
+ unsigned int oobbytes_per_page;
+ unsigned int dataleft;
+ unsigned int oobleft;
+};
+
+/**
+ * mtd_to_nanddev() - Get the NAND device attached to the MTD instance
+ * @mtd: MTD instance
+ *
+ * Return: the NAND device embedding @mtd.
+ */
+static inline struct nand_device *mtd_to_nanddev(struct mtd_info *mtd)
+{
+ return container_of(mtd, struct nand_device, mtd);
+}
+
+/**
+ * nanddev_to_mtd() - Get the MTD device attached to a NAND device
+ * @nand: NAND device
+ *
+ * Return: the MTD device embedded in @nand.
+ */
+static inline struct mtd_info *nanddev_to_mtd(struct nand_device *nand)
+{
+ return &nand->mtd;
+}
+
+/*
+ * nanddev_bits_per_cell() - Get the number of bits per cell
+ * @nand: NAND device
+ *
+ * Return: the number of bits per cell.
+ */
+static inline unsigned int nanddev_bits_per_cell(const struct nand_device *nand)
+{
+ return nand->memorg.bits_per_cell;
+}
+
+/**
+ * nanddev_page_size() - Get NAND page size
+ * @nand: NAND device
+ *
+ * Return: the page size.
+ */
+static inline size_t nanddev_page_size(const struct nand_device *nand)
+{
+ return nand->memorg.pagesize;
+}
+
+/**
+ * nanddev_per_page_oobsize() - Get NAND OOB size
+ * @nand: NAND device
+ *
+ * Return: the OOB size.
+ */
+static inline unsigned int
+nanddev_per_page_oobsize(const struct nand_device *nand)
+{
+ return nand->memorg.oobsize;
+}
+
+/**
+ * nanddev_pages_per_eraseblock() - Get the number of pages per eraseblock
+ * @nand: NAND device
+ *
+ * Return: the number of pages per eraseblock.
+ */
+static inline unsigned int
+nanddev_pages_per_eraseblock(const struct nand_device *nand)
+{
+ return nand->memorg.pages_per_eraseblock;
+}
+
+/**
+ * nanddev_per_page_oobsize() - Get NAND erase block size
+ * @nand: NAND device
+ *
+ * Return: the eraseblock size.
+ */
+static inline size_t nanddev_eraseblock_size(const struct nand_device *nand)
+{
+ return nand->memorg.pagesize * nand->memorg.pages_per_eraseblock;
+}
+
+/**
+ * nanddev_eraseblocks_per_lun() - Get the number of eraseblocks per LUN
+ * @nand: NAND device
+ *
+ * Return: the number of eraseblocks per LUN.
+ */
+static inline unsigned int
+nanddev_eraseblocks_per_lun(const struct nand_device *nand)
+{
+ return nand->memorg.eraseblocks_per_lun;
+}
+
+/**
+ * nanddev_target_size() - Get the total size provided by a single target/die
+ * @nand: NAND device
+ *
+ * Return: the total size exposed by a single target/die in bytes.
+ */
+static inline u64 nanddev_target_size(const struct nand_device *nand)
+{
+ return (u64)nand->memorg.luns_per_target *
+ nand->memorg.eraseblocks_per_lun *
+ nand->memorg.pages_per_eraseblock *
+ nand->memorg.pagesize;
+}
+
+/**
+ * nanddev_ntarget() - Get the total of targets
+ * @nand: NAND device
+ *
+ * Return: the number of targets/dies exposed by @nand.
+ */
+static inline unsigned int nanddev_ntargets(const struct nand_device *nand)
+{
+ return nand->memorg.ntargets;
+}
+
+/**
+ * nanddev_neraseblocks() - Get the total number of erasablocks
+ * @nand: NAND device
+ *
+ * Return: the total number of eraseblocks exposed by @nand.
+ */
+static inline unsigned int nanddev_neraseblocks(const struct nand_device *nand)
+{
+ return (u64)nand->memorg.luns_per_target *
+ nand->memorg.eraseblocks_per_lun *
+ nand->memorg.pages_per_eraseblock;
+}
+
+/**
+ * nanddev_size() - Get NAND size
+ * @nand: NAND device
+ *
+ * Return: the total size (in bytes) exposed by @nand.
+ */
+static inline u64 nanddev_size(const struct nand_device *nand)
+{
+ return nanddev_target_size(nand) * nanddev_ntargets(nand);
+}
+
+/**
+ * nanddev_get_memorg() - Extract memory organization info from a NAND device
+ * @nand: NAND device
+ *
+ * This can be used by the upper layer to fill the memorg info before calling
+ * nanddev_init().
+ *
+ * Return: the memorg object embedded in the NAND device.
+ */
+static inline struct nand_memory_organization *
+nanddev_get_memorg(struct nand_device *nand)
+{
+ return &nand->memorg;
+}
+
+int nanddev_init(struct nand_device *nand, const struct nand_ops *ops,
+ struct module *owner);
+void nanddev_cleanup(struct nand_device *nand);
+
+/**
+ * nanddev_register() - Register a NAND device
+ * @nand: NAND device
+ *
+ * Register a NAND device.
+ * This function is just a wrapper around mtd_device_register()
+ * registering the MTD device embedded in @nand.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+static inline int nanddev_register(struct nand_device *nand)
+{
+ return mtd_device_register(&nand->mtd, NULL, 0);
+}
+
+/**
+ * nanddev_unregister() - Unregister a NAND device
+ * @nand: NAND device
+ *
+ * Unregister a NAND device.
+ * This function is just a wrapper around mtd_device_unregister()
+ * unregistering the MTD device embedded in @nand.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+static inline int nanddev_unregister(struct nand_device *nand)
+{
+ return mtd_device_unregister(&nand->mtd);
+}
+
+/**
+ * nanddev_set_of_node() - Attach a DT node to a NAND device
+ * @nand: NAND device
+ * @np: DT node
+ *
+ * Attach a DT node to a NAND device.
+ */
+static inline void nanddev_set_of_node(struct nand_device *nand,
+ struct device_node *np)
+{
+ mtd_set_of_node(&nand->mtd, np);
+}
+
+/**
+ * nanddev_get_of_node() - Retrieve the DT node attached to a NAND device
+ * @nand: NAND device
+ *
+ * Return: the DT node attached to @nand.
+ */
+static inline struct device_node *nanddev_get_of_node(struct nand_device *nand)
+{
+ return mtd_get_of_node(&nand->mtd);
+}
+
+/**
+ * nanddev_offs_to_pos() - Convert an absolute NAND offset into a NAND position
+ * @nand: NAND device
+ * @offs: absolute NAND offset (usually passed by the MTD layer)
+ * @pos: a NAND position object to fill in
+ *
+ * Converts @offs into a nand_pos representation.
+ *
+ * Return: the offset within the NAND page pointed by @pos.
+ */
+static inline unsigned int nanddev_offs_to_pos(struct nand_device *nand,
+ loff_t offs,
+ struct nand_pos *pos)
+{
+ unsigned int pageoffs;
+ u64 tmp = offs;
+
+ pageoffs = do_div(tmp, nand->memorg.pagesize);
+ pos->page = do_div(tmp, nand->memorg.pages_per_eraseblock);
+ pos->eraseblock = do_div(tmp, nand->memorg.eraseblocks_per_lun);
+ pos->plane = pos->eraseblock % nand->memorg.planes_per_lun;
+ pos->lun = do_div(tmp, nand->memorg.luns_per_target);
+ pos->target = tmp;
+
+ return pageoffs;
+}
+
+/**
+ * nanddev_pos_cmp() - Compare two NAND positions
+ * @a: First NAND position
+ * @b: Second NAND position
+ *
+ * Compares two NAND positions.
+ *
+ * Return: -1 if @a < @b, 0 if @a == @b and 1 if @a > @b.
+ */
+static inline int nanddev_pos_cmp(const struct nand_pos *a,
+ const struct nand_pos *b)
+{
+ if (a->target != b->target)
+ return a->target < b->target ? -1 : 1;
+
+ if (a->lun != b->lun)
+ return a->lun < b->lun ? -1 : 1;
+
+ if (a->eraseblock != b->eraseblock)
+ return a->eraseblock < b->eraseblock ? -1 : 1;
+
+ if (a->page != b->page)
+ return a->page < b->page ? -1 : 1;
+
+ return 0;
+}
+
+/**
+ * nanddev_pos_to_offs() - Convert a NAND position into an absolute offset
+ * @nand: NAND device
+ * @pos: the NAND position to convert
+ *
+ * Converts @pos NAND position into an absolute offset.
+ *
+ * Return: the absolute offset. Note that @pos points to the beginning of a
+ * page, if one wants to point to a specific offset within this page
+ * the returned offset has to be adjusted manually.
+ */
+static inline loff_t nanddev_pos_to_offs(struct nand_device *nand,
+ const struct nand_pos *pos)
+{
+ unsigned int npages;
+
+ npages = pos->page +
+ ((pos->eraseblock +
+ (pos->lun +
+ (pos->target * nand->memorg.luns_per_target)) *
+ nand->memorg.eraseblocks_per_lun) *
+ nand->memorg.pages_per_eraseblock);
+
+ return (loff_t)npages * nand->memorg.pagesize;
+}
+
+/**
+ * nanddev_pos_to_row() - Extract a row address from a NAND position
+ * @nand: NAND device
+ * @pos: the position to convert
+ *
+ * Converts a NAND position into a row address that can then be passed to the
+ * device.
+ *
+ * Return: the row address extracted from @pos.
+ */
+static inline unsigned int nanddev_pos_to_row(struct nand_device *nand,
+ const struct nand_pos *pos)
+{
+ return (pos->lun << nand->rowconv.lun_addr_shift) |
+ (pos->eraseblock << nand->rowconv.eraseblock_addr_shift) |
+ pos->page;
+}
+
+/**
+ * nanddev_pos_next_target() - Move a position to the next target/die
+ * @nand: NAND device
+ * @pos: the position to update
+ *
+ * Updates @pos to point to the start of the next target/die. Useful when you
+ * want to iterate over all targets/dies of a NAND device.
+ */
+static inline void nanddev_pos_next_target(struct nand_device *nand,
+ struct nand_pos *pos)
+{
+ pos->page = 0;
+ pos->plane = 0;
+ pos->eraseblock = 0;
+ pos->lun = 0;
+ pos->target++;
+}
+
+/**
+ * nanddev_pos_next_lun() - Move a position to the next LUN
+ * @nand: NAND device
+ * @pos: the position to update
+ *
+ * Updates @pos to point to the start of the next LUN. Useful when you want to
+ * iterate over all LUNs of a NAND device.
+ */
+static inline void nanddev_pos_next_lun(struct nand_device *nand,
+ struct nand_pos *pos)
+{
+ if (pos->lun >= nand->memorg.luns_per_target - 1)
+ return nanddev_pos_next_target(nand, pos);
+
+ pos->lun++;
+ pos->page = 0;
+ pos->plane = 0;
+ pos->eraseblock = 0;
+}
+
+/**
+ * nanddev_pos_next_eraseblock() - Move a position to the next eraseblock
+ * @nand: NAND device
+ * @pos: the position to update
+ *
+ * Updates @pos to point to the start of the next eraseblock. Useful when you
+ * want to iterate over all eraseblocks of a NAND device.
+ */
+static inline void nanddev_pos_next_eraseblock(struct nand_device *nand,
+ struct nand_pos *pos)
+{
+ if (pos->eraseblock >= nand->memorg.eraseblocks_per_lun - 1)
+ return nanddev_pos_next_lun(nand, pos);
+
+ pos->eraseblock++;
+ pos->page = 0;
+ pos->plane = pos->eraseblock % nand->memorg.planes_per_lun;
+}
+
+/**
+ * nanddev_pos_next_eraseblock() - Move a position to the next page
+ * @nand: NAND device
+ * @pos: the position to update
+ *
+ * Updates @pos to point to the start of the next page. Useful when you want to
+ * iterate over all pages of a NAND device.
+ */
+static inline void nanddev_pos_next_page(struct nand_device *nand,
+ struct nand_pos *pos)
+{
+ if (pos->page >= nand->memorg.pages_per_eraseblock - 1)
+ return nanddev_pos_next_eraseblock(nand, pos);
+
+ pos->page++;
+}
+
+/**
+ * nand_io_iter_init - Initialize a NAND I/O iterator
+ * @nand: NAND device
+ * @offs: absolute offset
+ * @req: MTD request
+ * @iter: NAND I/O iterator
+ *
+ * Initializes a NAND iterator based on the information passed by the MTD
+ * layer.
+ */
+static inline void nanddev_io_iter_init(struct nand_device *nand,
+ loff_t offs, struct mtd_oob_ops *req,
+ struct nand_io_iter *iter)
+{
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+
+ iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos);
+ iter->req.ooboffs = req->ooboffs;
+ iter->oobbytes_per_page = mtd_oobavail(mtd, req);
+ iter->dataleft = req->len;
+ iter->oobleft = req->ooblen;
+ iter->req.databuf.in = req->datbuf;
+ iter->req.datalen = min_t(unsigned int,
+ nand->memorg.pagesize - iter->req.dataoffs,
+ iter->dataleft);
+ iter->req.oobbuf.in = req->oobbuf;
+ iter->req.ooblen = min_t(unsigned int,
+ iter->oobbytes_per_page - iter->req.ooboffs,
+ iter->oobleft);
+}
+
+/**
+ * nand_io_iter_next_page - Move to the next page
+ * @nand: NAND device
+ * @iter: NAND I/O iterator
+ *
+ * Updates the @iter to point to the next page.
+ */
+static inline void nanddev_io_iter_next_page(struct nand_device *nand,
+ struct nand_io_iter *iter)
+{
+ nanddev_pos_next_page(nand, &iter->req.pos);
+ iter->dataleft -= iter->req.datalen;
+ iter->req.databuf.in += iter->req.datalen;
+ iter->oobleft -= iter->req.ooblen;
+ iter->req.oobbuf.in += iter->req.ooblen;
+ iter->req.dataoffs = 0;
+ iter->req.ooboffs = 0;
+ iter->req.datalen = min_t(unsigned int, nand->memorg.pagesize,
+ iter->dataleft);
+ iter->req.ooblen = min_t(unsigned int, iter->oobbytes_per_page,
+ iter->oobleft);
+}
+
+/**
+ * nand_io_iter_end - Should end iteration or not
+ * @nand: NAND device
+ * @iter: NAND I/O iterator
+ *
+ * Check whether @iter has reached the end of the NAND portion it was asked to
+ * iterate on or not.
+ *
+ * Return: true if @iter has reached the end of the iteration request, false
+ * otherwise.
+ */
+static inline bool nanddev_io_iter_end(struct nand_device *nand,
+ const struct nand_io_iter *iter)
+{
+ if (iter->dataleft || iter->oobleft)
+ return false;
+
+ return true;
+}
+
+/**
+ * nand_io_for_each_page - Iterate over all NAND pages contained in an MTD I/O
+ * request
+ * @nand: NAND device
+ * @start: start address to read/write from
+ * @req: MTD I/O request
+ * @iter: NAND I/O iterator
+ *
+ * Should be used for iterate over pages that are contained in an MTD request.
+ */
+#define nanddev_io_for_each_page(nand, start, req, iter) \
+ for (nanddev_io_iter_init(nand, start, req, iter); \
+ !nanddev_io_iter_end(nand, iter); \
+ nanddev_io_iter_next_page(nand, iter))
+
+bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos);
+bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos);
+int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos);
+int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos);
+
+/* BBT related functions */
+enum nand_bbt_block_status {
+ NAND_BBT_BLOCK_STATUS_UNKNOWN,
+ NAND_BBT_BLOCK_GOOD,
+ NAND_BBT_BLOCK_WORN,
+ NAND_BBT_BLOCK_RESERVED,
+ NAND_BBT_BLOCK_FACTORY_BAD,
+ NAND_BBT_BLOCK_NUM_STATUS,
+};
+
+int nanddev_bbt_init(struct nand_device *nand);
+void nanddev_bbt_cleanup(struct nand_device *nand);
+int nanddev_bbt_update(struct nand_device *nand);
+int nanddev_bbt_get_block_status(const struct nand_device *nand,
+ unsigned int entry);
+int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry,
+ enum nand_bbt_block_status status);
+int nanddev_bbt_markbad(struct nand_device *nand, unsigned int block);
+
+/**
+ * nanddev_bbt_pos_to_entry() - Convert a NAND position into a BBT entry
+ * @nand: NAND device
+ * @pos: the NAND position we want to get BBT entry for
+ *
+ * Return the BBT entry used to store information about the eraseblock pointed
+ * by @pos.
+ *
+ * Return: the BBT entry storing information about eraseblock pointed by @pos.
+ */
+static inline unsigned int nanddev_bbt_pos_to_entry(struct nand_device *nand,
+ const struct nand_pos *pos)
+{
+ return pos->eraseblock +
+ ((pos->lun + (pos->target * nand->memorg.luns_per_target)) *
+ nand->memorg.eraseblocks_per_lun);
+}
+
+/**
+ * nanddev_bbt_is_initialized() - Check if the BBT has been initialized
+ * @nand: NAND device
+ *
+ * Return: true if the BBT has been initialized, false otherwise.
+ */
+static inline bool nanddev_bbt_is_initialized(struct nand_device *nand)
+{
+ return !!nand->bbt.cache;
+}
+
+/* MTD -> NAND helper functions. */
+int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo);
+
+#endif /* __LINUX_MTD_NAND_H */
diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h
index 4d8406c81652..8a2decf7462c 100644
--- a/include/linux/mtd/nand_ecc.h
+++ b/include/linux/mtd/nand_ecc.h
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand_ecc.h
- *
* Copyright (C) 2000-2010 Steven J. Hill <sjhill@realitydiluted.com>
* David Woodhouse <dwmw2@infradead.org>
* Thomas Gleixner <tglx@linutronix.de>
diff --git a/include/linux/mtd/ndfc.h b/include/linux/mtd/ndfc.h
index d0558a982628..357e88b3263a 100644
--- a/include/linux/mtd/ndfc.h
+++ b/include/linux/mtd/ndfc.h
@@ -1,6 +1,4 @@
/*
- * linux/include/linux/mtd/ndfc.h
- *
* Copyright (c) 2006 Thomas Gleixner <tglx@linutronix.de>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
index c4beb70dacbd..11cb0c50cd84 100644
--- a/include/linux/mtd/partitions.h
+++ b/include/linux/mtd/partitions.h
@@ -77,6 +77,7 @@ struct mtd_part_parser {
struct list_head list;
struct module *owner;
const char *name;
+ const struct of_device_id *of_match_table;
int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
struct mtd_part_parser_data *);
void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
index 56c5570aadbe..5dad59b31244 100644
--- a/include/linux/mtd/rawnand.h
+++ b/include/linux/mtd/rawnand.h
@@ -21,6 +21,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/flashchip.h>
#include <linux/mtd/bbm.h>
+#include <linux/types.h>
struct mtd_info;
struct nand_flash_dev;
@@ -235,7 +236,8 @@ struct nand_chip;
#define ONFI_TIMING_MODE_5 (1 << 5)
#define ONFI_TIMING_MODE_UNKNOWN (1 << 6)
-/* ONFI feature address */
+/* ONFI feature number/address */
+#define ONFI_FEATURE_NUMBER 256
#define ONFI_FEATURE_ADDR_TIMING_MODE 0x1
/* Vendor-specific feature address (Micron) */
@@ -429,6 +431,47 @@ struct nand_jedec_params {
__le16 crc;
} __packed;
+/**
+ * struct onfi_params - ONFI specific parameters that will be reused
+ * @version: ONFI version (BCD encoded), 0 if ONFI is not supported
+ * @tPROG: Page program time
+ * @tBERS: Block erase time
+ * @tR: Page read time
+ * @tCCS: Change column setup time
+ * @async_timing_mode: Supported asynchronous timing mode
+ * @vendor_revision: Vendor specific revision number
+ * @vendor: Vendor specific data
+ */
+struct onfi_params {
+ int version;
+ u16 tPROG;
+ u16 tBERS;
+ u16 tR;
+ u16 tCCS;
+ u16 async_timing_mode;
+ u16 vendor_revision;
+ u8 vendor[88];
+};
+
+/**
+ * struct nand_parameters - NAND generic parameters from the parameter page
+ * @model: Model name
+ * @supports_set_get_features: The NAND chip supports setting/getting features
+ * @set_feature_list: Bitmap of features that can be set
+ * @get_feature_list: Bitmap of features that can be get
+ * @onfi: ONFI specific parameters
+ */
+struct nand_parameters {
+ /* Generic parameters */
+ char model[100];
+ bool supports_set_get_features;
+ DECLARE_BITMAP(set_feature_list, ONFI_FEATURE_NUMBER);
+ DECLARE_BITMAP(get_feature_list, ONFI_FEATURE_NUMBER);
+
+ /* ONFI parameters */
+ struct onfi_params onfi;
+};
+
/* The maximum expected count of bytes in the NAND ID sequence */
#define NAND_MAX_ID_LEN 8
@@ -1157,21 +1200,15 @@ int nand_op_parser_exec_op(struct nand_chip *chip,
* currently in data_buf.
* @subpagesize: [INTERN] holds the subpagesize
* @id: [INTERN] holds NAND ID
- * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
- * non 0 if ONFI supported.
- * @jedec_version: [INTERN] holds the chip JEDEC version (BCD encoded),
- * non 0 if JEDEC supported.
- * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is
- * supported, 0 otherwise.
- * @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is
- * supported, 0 otherwise.
+ * @parameters: [INTERN] holds generic parameters under an easily
+ * readable form.
* @max_bb_per_die: [INTERN] the max number of bad blocks each die of a
* this nand device will encounter their life times.
* @blocks_per_die: [INTERN] The number of PEBs in a die
* @data_interface: [INTERN] NAND interface timing information
* @read_retries: [INTERN] the number of read retry modes supported
- * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
- * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
+ * @set_features: [REPLACEABLE] set the NAND chip features
+ * @get_features: [REPLACEABLE] get the NAND chip features
* @setup_data_interface: [OPTIONAL] setup the data interface and timing. If
* chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this
* means the configuration should not be applied but
@@ -1212,10 +1249,10 @@ struct nand_chip {
bool check_only);
int (*erase)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
- int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
- int feature_addr, uint8_t *subfeature_para);
- int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
- int feature_addr, uint8_t *subfeature_para);
+ int (*set_features)(struct mtd_info *mtd, struct nand_chip *chip,
+ int feature_addr, uint8_t *subfeature_para);
+ int (*get_features)(struct mtd_info *mtd, struct nand_chip *chip,
+ int feature_addr, uint8_t *subfeature_para);
int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
int (*setup_data_interface)(struct mtd_info *mtd, int chipnr,
const struct nand_data_interface *conf);
@@ -1243,12 +1280,7 @@ struct nand_chip {
int badblockbits;
struct nand_id id;
- int onfi_version;
- int jedec_version;
- union {
- struct nand_onfi_params onfi_params;
- struct nand_jedec_params jedec_params;
- };
+ struct nand_parameters parameters;
u16 max_bb_per_die;
u32 blocks_per_die;
@@ -1535,26 +1567,13 @@ struct platform_nand_data {
struct platform_nand_ctrl ctrl;
};
-/* return the supported features. */
-static inline int onfi_feature(struct nand_chip *chip)
-{
- return chip->onfi_version ? le16_to_cpu(chip->onfi_params.features) : 0;
-}
-
/* return the supported asynchronous timing mode. */
static inline int onfi_get_async_timing_mode(struct nand_chip *chip)
{
- if (!chip->onfi_version)
+ if (!chip->parameters.onfi.version)
return ONFI_TIMING_MODE_UNKNOWN;
- return le16_to_cpu(chip->onfi_params.async_timing_mode);
-}
-/* return the supported synchronous timing mode. */
-static inline int onfi_get_sync_timing_mode(struct nand_chip *chip)
-{
- if (!chip->onfi_version)
- return ONFI_TIMING_MODE_UNKNOWN;
- return le16_to_cpu(chip->onfi_params.src_sync_timing_mode);
+ return chip->parameters.onfi.async_timing_mode;
}
int onfi_fill_data_interface(struct nand_chip *chip,
@@ -1591,13 +1610,6 @@ static inline int nand_opcode_8bits(unsigned int command)
return 0;
}
-/* return the supported JEDEC features. */
-static inline int jedec_feature(struct nand_chip *chip)
-{
- return chip->jedec_version ? le16_to_cpu(chip->jedec_params.features)
- : 0;
-}
-
/* get timing characteristics from ONFI timing mode. */
const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
@@ -1629,10 +1641,12 @@ int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);
int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
int page);
+/* Wrapper to use in order for controllers/vendors to GET/SET FEATURES */
+int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
+int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
/* Stub used by drivers that do not support GET/SET FEATURES operations */
-int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd,
- struct nand_chip *chip, int addr,
- u8 *subfeature_param);
+int nand_get_set_features_notsupp(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, u8 *subfeature_param);
/* Default read_page_raw implementation */
int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,