Merge tag 'for-linus-20160523' of git://git.infradead.org/linux-mtd
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 24 May 2016 18:00:20 +0000 (11:00 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 24 May 2016 18:00:20 +0000 (11:00 -0700)
Pull MTD updates from Brian Norris:
 "First cycle with Boris as NAND maintainer! Many (most) bullets stolen
  from him.

  Generic:
   - Migrated NAND LED trigger to be a generic MTD trigger

  NAND:
   - Introduction of the "ECC algorithm" concept, to avoid overloading
     the ECC mode field too much more
   - Replaced the nand_ecclayout infrastructure with something a little
     more flexible (finally!) and future proof
   - Rework of the OMAP GPMC and NAND drivers; the TI folks pulled some
     of this into their own tree as well
   - Prepare the sunxi NAND driver to receive DMA support
   - Handle bitflips in erased pages on GPMI revisions that do not
     support this in hardware.

  SPI NOR:
   - Start using the spi_flash_read() API for SPI drivers that support
     it (i.e., SPI drivers with special memory-mapped flash modes)

  And other small scattered improvments"

* tag 'for-linus-20160523' of git://git.infradead.org/linux-mtd: (155 commits)
  mtd: spi-nor: support GigaDevice gd25lq64c
  mtd: nand_bch: fix spelling of "probably"
  mtd: brcmnand: respect ECC algorithm set by NAND subsystem
  gpmi-nand: Handle ECC Errors in erased pages
  Documentation: devicetree: deprecate "soft_bch" nand-ecc-mode value
  mtd: nand: add support for "nand-ecc-algo" DT property
  mtd: mtd: drop NAND_ECC_SOFT_BCH enum value
  mtd: drop support for NAND_ECC_SOFT_BCH as "soft_bch" mapping
  mtd: nand: read ECC algorithm from the new field
  mtd: nand: fsmc: validate ECC setup by checking algorithm directly
  mtd: nand: set ECC algorithm to Hamming on fallback
  staging: mt29f_spinand: set ECC algorithm explicitly
  CRIS v32: nand: set ECC algorithm explicitly
  mtd: nand: atmel: set ECC algorithm explicitly
  mtd: nand: davinci: set ECC algorithm explicitly
  mtd: nand: bf5xx: set ECC algorithm explicitly
  mtd: nand: omap2: Fix high memory dma prefetch transfer
  mtd: nand: omap2: Start dma request before enabling prefetch
  mtd: nandsim: add __init attribute
  mtd: nand: move of_get_nand_xxx() helpers into nand_base.c
  ...

90 files changed:
Documentation/devicetree/bindings/bus/ti-gpmc.txt [deleted file]
Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt
Documentation/devicetree/bindings/mtd/gpmc-nand.txt
Documentation/devicetree/bindings/mtd/nand.txt
arch/arm/mach-omap2/gpmc-nand.c
arch/arm/mach-pxa/spitz.c
arch/cris/arch-v32/drivers/mach-a3/nandflash.c
arch/cris/arch-v32/drivers/mach-fs/nandflash.c
arch/mips/include/asm/mach-jz4740/jz4740_nand.h
arch/mips/jz4740/board-qi_lb60.c
drivers/bcma/driver_chipcommon_sflash.c
drivers/memory/Kconfig
drivers/memory/fsl_ifc.c
drivers/memory/omap-gpmc.c
drivers/mtd/chips/Kconfig
drivers/mtd/devices/bcm47xxsflash.c
drivers/mtd/devices/bcm47xxsflash.h
drivers/mtd/devices/docg3.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/pmc551.c
drivers/mtd/maps/ck804xrom.c
drivers/mtd/maps/esb2rom.c
drivers/mtd/maps/ichxrom.c
drivers/mtd/maps/uclinux.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/ams-delta.c
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/au1550nd.c
drivers/mtd/nand/bf5xx_nand.c
drivers/mtd/nand/brcmnand/brcmnand.c
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/cmx270_nand.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/denali.c
drivers/mtd/nand/diskonchip.c
drivers/mtd/nand/docg4.c
drivers/mtd/nand/fsl_elbc_nand.c
drivers/mtd/nand/fsl_ifc_nand.c
drivers/mtd/nand/fsl_upm.c
drivers/mtd/nand/fsmc_nand.c
drivers/mtd/nand/gpio.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/hisi504_nand.c
drivers/mtd/nand/jz4740_nand.c
drivers/mtd/nand/jz4780_bch.c
drivers/mtd/nand/jz4780_nand.c
drivers/mtd/nand/lpc32xx_mlc.c
drivers/mtd/nand/lpc32xx_slc.c
drivers/mtd/nand/mpc5121_nfc.c
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_bch.c
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/nuc900_nand.c
drivers/mtd/nand/omap2.c
drivers/mtd/nand/orion_nand.c
drivers/mtd/nand/pasemi_nand.c
drivers/mtd/nand/plat_nand.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/mtd/nand/qcom_nandc.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/nand/sh_flctl.c
drivers/mtd/nand/sharpsl.c
drivers/mtd/nand/sm_common.c
drivers/mtd/nand/socrates_nand.c
drivers/mtd/nand/sunxi_nand.c
drivers/mtd/nand/vf610_nfc.c
drivers/mtd/onenand/onenand_base.c
drivers/mtd/spi-nor/spi-nor.c
drivers/of/Makefile
drivers/of/of_mtd.c [deleted file]
drivers/staging/mt29f_spinand/mt29f_spinand.c
include/linux/bcma/bcma_driver_chipcommon.h
include/linux/fsl_ifc.h
include/linux/mtd/fsmc.h
include/linux/mtd/map.h
include/linux/mtd/mtd.h
include/linux/mtd/nand.h
include/linux/mtd/onenand.h
include/linux/mtd/sharpsl.h
include/linux/mtd/spi-nor.h
include/linux/of_mtd.h [deleted file]
include/linux/omap-gpmc.h
include/linux/platform_data/gpmc-omap.h [new file with mode: 0644]
include/linux/platform_data/mtd-nand-omap2.h
include/uapi/mtd/mtd-abi.h

diff --git a/Documentation/devicetree/bindings/bus/ti-gpmc.txt b/Documentation/devicetree/bindings/bus/ti-gpmc.txt
deleted file mode 100644 (file)
index 0168370..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-Device tree bindings for OMAP general purpose memory controllers (GPMC)
-
-The actual devices are instantiated from the child nodes of a GPMC node.
-
-Required properties:
-
- - compatible:         Should be set to one of the following:
-
-                       ti,omap2420-gpmc (omap2420)
-                       ti,omap2430-gpmc (omap2430)
-                       ti,omap3430-gpmc (omap3430 & omap3630)
-                       ti,omap4430-gpmc (omap4430 & omap4460 & omap543x)
-                       ti,am3352-gpmc   (am335x devices)
-
- - reg:                        A resource specifier for the register space
-                       (see the example below)
- - ti,hwmods:          Should be set to "ti,gpmc" until the DT transition is
-                       completed.
- - #address-cells:     Must be set to 2 to allow memory address translation
- - #size-cells:                Must be set to 1 to allow CS address passing
- - gpmc,num-cs:                The maximum number of chip-select lines that controller
-                       can support.
- - gpmc,num-waitpins:  The maximum number of wait pins that controller can
-                       support.
- - ranges:             Must be set up to reflect the memory layout with four
-                       integer values for each chip-select line in use:
-
-                          <cs-number> 0 <physical address of mapping> <size>
-
-                       Currently, calculated values derived from the contents
-                       of the per-CS register GPMC_CONFIG7 (as set up by the
-                       bootloader) are used for the physical address decoding.
-                       As this will change in the future, filling correct
-                       values here is a requirement.
-
-Timing properties for child nodes. All are optional and default to 0.
-
- - gpmc,sync-clk-ps:   Minimum clock period for synchronous mode, in picoseconds
-
- Chip-select signal timings (in nanoseconds) corresponding to GPMC_CONFIG2:
- - gpmc,cs-on-ns:      Assertion time
- - gpmc,cs-rd-off-ns:  Read deassertion time
- - gpmc,cs-wr-off-ns:  Write deassertion time
-
- ADV signal timings (in nanoseconds) corresponding to GPMC_CONFIG3:
- - gpmc,adv-on-ns:     Assertion time
- - gpmc,adv-rd-off-ns: Read deassertion time
- - gpmc,adv-wr-off-ns: Write deassertion time
- - gpmc,adv-aad-mux-on-ns:     Assertion time for AAD
- - gpmc,adv-aad-mux-rd-off-ns: Read deassertion time for AAD
- - gpmc,adv-aad-mux-wr-off-ns: Write deassertion time for AAD
-
- WE signals timings (in nanoseconds) corresponding to GPMC_CONFIG4:
- - gpmc,we-on-ns       Assertion time
- - gpmc,we-off-ns:     Deassertion time
-
- OE signals timings (in nanoseconds) corresponding to GPMC_CONFIG4:
- - gpmc,oe-on-ns:      Assertion time
- - gpmc,oe-off-ns:     Deassertion time
- - gpmc,oe-aad-mux-on-ns:      Assertion time for AAD
- - gpmc,oe-aad-mux-off-ns:     Deassertion time for AAD
-
- Access time and cycle time timings (in nanoseconds) corresponding to
- GPMC_CONFIG5:
- - gpmc,page-burst-access-ns:  Multiple access word delay
- - gpmc,access-ns:             Start-cycle to first data valid delay
- - gpmc,rd-cycle-ns:           Total read cycle time
- - gpmc,wr-cycle-ns:           Total write cycle time
- - gpmc,bus-turnaround-ns:     Turn-around time between successive accesses
- - gpmc,cycle2cycle-delay-ns:  Delay between chip-select pulses
- - gpmc,clk-activation-ns:     GPMC clock activation time
- - gpmc,wait-monitoring-ns:    Start of wait monitoring with regard to valid
-                               data
-
-Boolean timing parameters. If property is present parameter enabled and
-disabled if omitted:
- - gpmc,adv-extra-delay:       ADV signal is delayed by half GPMC clock
- - gpmc,cs-extra-delay:                CS signal is delayed by half GPMC clock
- - gpmc,cycle2cycle-diffcsen:  Add "cycle2cycle-delay" between successive
-                               accesses to a different CS
- - gpmc,cycle2cycle-samecsen:  Add "cycle2cycle-delay" between successive
-                               accesses to the same CS
- - gpmc,oe-extra-delay:                OE signal is delayed by half GPMC clock
- - gpmc,we-extra-delay:                WE signal is delayed by half GPMC clock
- - gpmc,time-para-granularity: Multiply all access times by 2
-
-The following are only applicable to OMAP3+ and AM335x:
- - gpmc,wr-access-ns:          In synchronous write mode, for single or
-                               burst accesses, defines the number of
-                               GPMC_FCLK cycles from start access time
-                               to the GPMC_CLK rising edge used by the
-                               memory device for the first data capture.
- - gpmc,wr-data-mux-bus-ns:    In address-data multiplex mode, specifies
-                               the time when the first data is driven on
-                               the address-data bus.
-
-GPMC chip-select settings properties for child nodes. All are optional.
-
-- gpmc,burst-length    Page/burst length. Must be 4, 8 or 16.
-- gpmc,burst-wrap      Enables wrap bursting
-- gpmc,burst-read      Enables read page/burst mode
-- gpmc,burst-write     Enables write page/burst mode
-- gpmc,device-width    Total width of device(s) connected to a GPMC
-                       chip-select in bytes. The GPMC supports 8-bit
-                       and 16-bit devices and so this property must be
-                       1 or 2.
-- gpmc,mux-add-data    Address and data multiplexing configuration.
-                       Valid values are 1 for address-address-data
-                       multiplexing mode and 2 for address-data
-                       multiplexing mode.
-- gpmc,sync-read       Enables synchronous read. Defaults to asynchronous
-                       is this is not set.
-- gpmc,sync-write      Enables synchronous writes. Defaults to asynchronous
-                       is this is not set.
-- gpmc,wait-pin                Wait-pin used by client. Must be less than
-                       "gpmc,num-waitpins".
-- gpmc,wait-on-read    Enables wait monitoring on reads.
-- gpmc,wait-on-write   Enables wait monitoring on writes.
-
-Example for an AM33xx board:
-
-       gpmc: gpmc@50000000 {
-               compatible = "ti,am3352-gpmc";
-               ti,hwmods = "gpmc";
-               reg = <0x50000000 0x2000>;
-               interrupts = <100>;
-
-               gpmc,num-cs = <8>;
-               gpmc,num-waitpins = <2>;
-               #address-cells = <2>;
-               #size-cells = <1>;
-               ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */
-
-               /* child nodes go here */
-       };
diff --git a/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt b/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt
new file mode 100644 (file)
index 0000000..21055e2
--- /dev/null
@@ -0,0 +1,152 @@
+Device tree bindings for OMAP general purpose memory controllers (GPMC)
+
+The actual devices are instantiated from the child nodes of a GPMC node.
+
+Required properties:
+
+ - compatible:         Should be set to one of the following:
+
+                       ti,omap2420-gpmc (omap2420)
+                       ti,omap2430-gpmc (omap2430)
+                       ti,omap3430-gpmc (omap3430 & omap3630)
+                       ti,omap4430-gpmc (omap4430 & omap4460 & omap543x)
+                       ti,am3352-gpmc   (am335x devices)
+
+ - reg:                        A resource specifier for the register space
+                       (see the example below)
+ - ti,hwmods:          Should be set to "ti,gpmc" until the DT transition is
+                       completed.
+ - #address-cells:     Must be set to 2 to allow memory address translation
+ - #size-cells:                Must be set to 1 to allow CS address passing
+ - gpmc,num-cs:                The maximum number of chip-select lines that controller
+                       can support.
+ - gpmc,num-waitpins:  The maximum number of wait pins that controller can
+                       support.
+ - ranges:             Must be set up to reflect the memory layout with four
+                       integer values for each chip-select line in use:
+
+                          <cs-number> 0 <physical address of mapping> <size>
+
+                       Currently, calculated values derived from the contents
+                       of the per-CS register GPMC_CONFIG7 (as set up by the
+                       bootloader) are used for the physical address decoding.
+                       As this will change in the future, filling correct
+                       values here is a requirement.
+ - interrupt-controller: The GPMC driver implements and interrupt controller for
+                       the NAND events "fifoevent" and "termcount" plus the
+                       rising/falling edges on the GPMC_WAIT pins.
+                       The interrupt number mapping is as follows
+                       0 - NAND_fifoevent
+                       1 - NAND_termcount
+                       2 - GPMC_WAIT0 pin edge
+                       3 - GPMC_WAIT1 pin edge, and so on.
+ - interrupt-cells:    Must be set to 2
+ - gpio-controller:    The GPMC driver implements a GPIO controller for the
+                       GPMC WAIT pins that can be used as general purpose inputs.
+                       0 maps to GPMC_WAIT0 pin.
+ - gpio-cells:         Must be set to 2
+
+Timing properties for child nodes. All are optional and default to 0.
+
+ - gpmc,sync-clk-ps:   Minimum clock period for synchronous mode, in picoseconds
+
+ Chip-select signal timings (in nanoseconds) corresponding to GPMC_CONFIG2:
+ - gpmc,cs-on-ns:      Assertion time
+ - gpmc,cs-rd-off-ns:  Read deassertion time
+ - gpmc,cs-wr-off-ns:  Write deassertion time
+
+ ADV signal timings (in nanoseconds) corresponding to GPMC_CONFIG3:
+ - gpmc,adv-on-ns:     Assertion time
+ - gpmc,adv-rd-off-ns: Read deassertion time
+ - gpmc,adv-wr-off-ns: Write deassertion time
+ - gpmc,adv-aad-mux-on-ns:     Assertion time for AAD
+ - gpmc,adv-aad-mux-rd-off-ns: Read deassertion time for AAD
+ - gpmc,adv-aad-mux-wr-off-ns: Write deassertion time for AAD
+
+ WE signals timings (in nanoseconds) corresponding to GPMC_CONFIG4:
+ - gpmc,we-on-ns       Assertion time
+ - gpmc,we-off-ns:     Deassertion time
+
+ OE signals timings (in nanoseconds) corresponding to GPMC_CONFIG4:
+ - gpmc,oe-on-ns:      Assertion time
+ - gpmc,oe-off-ns:     Deassertion time
+ - gpmc,oe-aad-mux-on-ns:      Assertion time for AAD
+ - gpmc,oe-aad-mux-off-ns:     Deassertion time for AAD
+
+ Access time and cycle time timings (in nanoseconds) corresponding to
+ GPMC_CONFIG5:
+ - gpmc,page-burst-access-ns:  Multiple access word delay
+ - gpmc,access-ns:             Start-cycle to first data valid delay
+ - gpmc,rd-cycle-ns:           Total read cycle time
+ - gpmc,wr-cycle-ns:           Total write cycle time
+ - gpmc,bus-turnaround-ns:     Turn-around time between successive accesses
+ - gpmc,cycle2cycle-delay-ns:  Delay between chip-select pulses
+ - gpmc,clk-activation-ns:     GPMC clock activation time
+ - gpmc,wait-monitoring-ns:    Start of wait monitoring with regard to valid
+                               data
+
+Boolean timing parameters. If property is present parameter enabled and
+disabled if omitted:
+ - gpmc,adv-extra-delay:       ADV signal is delayed by half GPMC clock
+ - gpmc,cs-extra-delay:                CS signal is delayed by half GPMC clock
+ - gpmc,cycle2cycle-diffcsen:  Add "cycle2cycle-delay" between successive
+                               accesses to a different CS
+ - gpmc,cycle2cycle-samecsen:  Add "cycle2cycle-delay" between successive
+                               accesses to the same CS
+ - gpmc,oe-extra-delay:                OE signal is delayed by half GPMC clock
+ - gpmc,we-extra-delay:                WE signal is delayed by half GPMC clock
+ - gpmc,time-para-granularity: Multiply all access times by 2
+
+The following are only applicable to OMAP3+ and AM335x:
+ - gpmc,wr-access-ns:          In synchronous write mode, for single or
+                               burst accesses, defines the number of
+                               GPMC_FCLK cycles from start access time
+                               to the GPMC_CLK rising edge used by the
+                               memory device for the first data capture.
+ - gpmc,wr-data-mux-bus-ns:    In address-data multiplex mode, specifies
+                               the time when the first data is driven on
+                               the address-data bus.
+
+GPMC chip-select settings properties for child nodes. All are optional.
+
+- gpmc,burst-length    Page/burst length. Must be 4, 8 or 16.
+- gpmc,burst-wrap      Enables wrap bursting
+- gpmc,burst-read      Enables read page/burst mode
+- gpmc,burst-write     Enables write page/burst mode
+- gpmc,device-width    Total width of device(s) connected to a GPMC
+                       chip-select in bytes. The GPMC supports 8-bit
+                       and 16-bit devices and so this property must be
+                       1 or 2.
+- gpmc,mux-add-data    Address and data multiplexing configuration.
+                       Valid values are 1 for address-address-data
+                       multiplexing mode and 2 for address-data
+                       multiplexing mode.
+- gpmc,sync-read       Enables synchronous read. Defaults to asynchronous
+                       is this is not set.
+- gpmc,sync-write      Enables synchronous writes. Defaults to asynchronous
+                       is this is not set.
+- gpmc,wait-pin                Wait-pin used by client. Must be less than
+                       "gpmc,num-waitpins".
+- gpmc,wait-on-read    Enables wait monitoring on reads.
+- gpmc,wait-on-write   Enables wait monitoring on writes.
+
+Example for an AM33xx board:
+
+       gpmc: gpmc@50000000 {
+               compatible = "ti,am3352-gpmc";
+               ti,hwmods = "gpmc";
+               reg = <0x50000000 0x2000>;
+               interrupts = <100>;
+
+               gpmc,num-cs = <8>;
+               gpmc,num-waitpins = <2>;
+               #address-cells = <2>;
+               #size-cells = <1>;
+               ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */
+               interrupt-controller;
+               #interrupt-cells = <2>;
+               gpio-controller;
+               #gpio-cells = <2>;
+
+               /* child nodes go here */
+       };
index 0f6985b..7066597 100644 (file)
@@ -24,6 +24,7 @@ Required properties:
                          brcm,brcmnand-v5.0
                          brcm,brcmnand-v6.0
                          brcm,brcmnand-v6.1
+                         brcm,brcmnand-v6.2
                          brcm,brcmnand-v7.0
                          brcm,brcmnand-v7.1
                          brcm,brcmnand
index fb733c4..3ee7e20 100644 (file)
@@ -13,7 +13,11 @@ Documentation/devicetree/bindings/mtd/nand.txt
 
 Required properties:
 
- - reg:                The CS line the peripheral is connected to
+ - compatible: "ti,omap2-nand"
+ - reg:                range id (CS number), base offset and length of the
+               NAND I/O space
+ - interrupt-parent: must point to gpmc node
+ - interrupts: Two interrupt specifiers, one for fifoevent, one for termcount.
 
 Optional properties:
 
@@ -44,6 +48,7 @@ Optional properties:
                locating ECC errors for BCHx algorithms. SoC devices which have
                ELM hardware engines should specify this device node in .dtsi
                Using ELM for ECC error correction frees some CPU cycles.
+ - rb-gpios:   GPIO specifier for the ready/busy# pin.
 
 For inline partition table parsing (optional):
 
@@ -55,20 +60,26 @@ Example for an AM33xx board:
        gpmc: gpmc@50000000 {
                compatible = "ti,am3352-gpmc";
                ti,hwmods = "gpmc";
-               reg = <0x50000000 0x1000000>;
+               reg = <0x50000000 0x36c>;
                interrupts = <100>;
                gpmc,num-cs = <8>;
                gpmc,num-waitpins = <2>;
                #address-cells = <2>;
                #size-cells = <1>;
-               ranges = <0 0 0x08000000 0x2000>;       /* CS0: NAND */
+               ranges = <0 0 0x08000000 0x1000000>;    /* CS0 space, 16MB */
                elm_id = <&elm>;
+               interrupt-controller;
+               #interrupt-cells = <2>;
 
                nand@0,0 {
-                       reg = <0 0 0>; /* CS0, offset 0 */
+                       compatible = "ti,omap2-nand";
+                       reg = <0 0 4>;          /* CS0, offset 0, NAND I/O window 4 */
+                       interrupt-parent = <&gpmc>;
+                       interrupts = <0 IRQ_TYPE_NONE>, <1 IRQ_TYPE NONE>;
                        nand-bus-width = <16>;
                        ti,nand-ecc-opt = "bch8";
                        ti,nand-xfer-type = "polled";
+                       rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */
 
                        gpmc,sync-clk-ps = <0>;
                        gpmc,cs-on-ns = <0>;
index b53f92e..68342ea 100644 (file)
@@ -1,8 +1,31 @@
-* MTD generic binding
+* NAND chip and NAND controller generic binding
+
+NAND controller/NAND chip representation:
+
+The NAND controller should be represented with its own DT node, and all
+NAND chips attached to this controller should be defined as children nodes
+of the NAND controller. This representation should be enforced even for
+simple controllers supporting only one chip.
+
+Mandatory NAND controller properties:
+- #address-cells: depends on your controller. Should at least be 1 to
+                 encode the CS line id.
+- #size-cells: depends on your controller. Put zero unless you need a
+              mapping between CS lines and dedicated memory regions
+
+Optional NAND controller properties
+- ranges: only needed if you need to define a mapping between CS lines and
+         memory regions
+
+Optional NAND chip properties:
 
 - nand-ecc-mode : String, operation mode of the NAND ecc mode.
-  Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
-  "soft_bch".
+                 Supported values are: "none", "soft", "hw", "hw_syndrome",
+                 "hw_oob_first".
+                 Deprecated values:
+                 "soft_bch": use "soft" and nand-ecc-algo instead
+- nand-ecc-algo: string, algorithm of NAND ECC.
+                Supported values are: "hamming", "bch".
 - nand-bus-width : 8 or 16 bus width if not present 8
 - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
 
@@ -19,3 +42,19 @@ errors per {size} bytes".
 The interpretation of these parameters is implementation-defined, so not all
 implementations must support all possible combinations. However, implementations
 are encouraged to further specify the value(s) they support.
+
+Example:
+
+       nand-controller {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               /* controller specific properties */
+
+               nand@0 {
+                       reg = <0>;
+                       nand-ecc-mode = "soft_bch";
+
+                       /* controller specific properties */
+               };
+       };
index 72918c4..f6ac027 100644 (file)
@@ -97,10 +97,7 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
        gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT);
 
        memset(&s, 0, sizeof(struct gpmc_settings));
-       if (gpmc_nand_data->of_node)
-               gpmc_read_settings_dt(gpmc_nand_data->of_node, &s);
-       else
-               gpmc_set_legacy(gpmc_nand_data, &s);
+       gpmc_set_legacy(gpmc_nand_data, &s);
 
        s.device_nand = true;
 
@@ -121,8 +118,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
        if (err < 0)
                goto out_free_cs;
 
-       gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs);
-
        if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt)) {
                pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n");
                err = -EINVAL;
index d9578bc..bd7cd8b 100644 (file)
@@ -763,14 +763,49 @@ static struct nand_bbt_descr spitz_nand_bbt = {
        .pattern        = scan_ff_pattern
 };
 
-static struct nand_ecclayout akita_oobinfo = {
-       .oobfree        = { {0x08, 0x09} },
-       .eccbytes       = 24,
-       .eccpos         = {
-                       0x05, 0x01, 0x02, 0x03, 0x06, 0x07, 0x15, 0x11,
-                       0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
-                       0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37,
-       },
+static int akita_ooblayout_ecc(struct mtd_info *mtd, int section,
+                              struct mtd_oob_region *oobregion)
+{
+       if (section > 12)
+               return -ERANGE;
+
+       switch (section % 3) {
+       case 0:
+               oobregion->offset = 5;
+               oobregion->length = 1;
+               break;
+
+       case 1:
+               oobregion->offset = 1;
+               oobregion->length = 3;
+               break;
+
+       case 2:
+               oobregion->offset = 6;
+               oobregion->length = 2;
+               break;
+       }
+
+       oobregion->offset += (section / 3) * 0x10;
+
+       return 0;
+}
+
+static int akita_ooblayout_free(struct mtd_info *mtd, int section,
+                               struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 8;
+       oobregion->length = 9;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops akita_ooblayout_ops = {
+       .ecc = akita_ooblayout_ecc,
+       .free = akita_ooblayout_free,
 };
 
 static struct sharpsl_nand_platform_data spitz_nand_pdata = {
@@ -804,11 +839,11 @@ static void __init spitz_nand_init(void)
        } else if (machine_is_akita()) {
                spitz_nand_partitions[1].size = 58 * 1024 * 1024;
                spitz_nand_bbt.len = 1;
-               spitz_nand_pdata.ecc_layout = &akita_oobinfo;
+               spitz_nand_pdata.ecc_layout = &akita_ooblayout_ops;
        } else if (machine_is_borzoi()) {
                spitz_nand_partitions[1].size = 32 * 1024 * 1024;
                spitz_nand_bbt.len = 1;
-               spitz_nand_pdata.ecc_layout = &akita_oobinfo;
+               spitz_nand_pdata.ecc_layout = &akita_ooblayout_ops;
        }
 
        platform_device_register(&spitz_nand_device);
index 5aa3f51..3f646c7 100644 (file)
@@ -157,6 +157,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void)
        /* 20 us command delay time */
        this->chip_delay = 20;
        this->ecc.mode = NAND_ECC_SOFT;
+       this->ecc.algo = NAND_ECC_HAMMING;
 
        /* Enable the following for a flash based bad block table */
        /* this->bbt_options = NAND_BBT_USE_FLASH; */
index a7c17b0..a745405 100644 (file)
@@ -148,6 +148,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void)
        /* 20 us command delay time */
        this->chip_delay = 20;
        this->ecc.mode = NAND_ECC_SOFT;
+       this->ecc.algo = NAND_ECC_HAMMING;
 
        /* Enable the following for a flash based bad block table */
        /* this->bbt_options = NAND_BBT_USE_FLASH; */
index 398733e..7f7b0fc 100644 (file)
@@ -27,7 +27,7 @@ struct jz_nand_platform_data {
 
        unsigned char banks[JZ_NAND_NUM_BANKS];
 
-       void (*ident_callback)(struct platform_device *, struct nand_chip *,
+       void (*ident_callback)(struct platform_device *, struct mtd_info *,
                                struct mtd_partition **, int *num_partitions);
 };
 
index 4e3f9b7..258fd03 100644 (file)
 #define QI_LB60_GPIO_KEYIN8            JZ_GPIO_PORTD(26)
 
 /* NAND */
-static struct nand_ecclayout qi_lb60_ecclayout_1gb = {
-       .eccbytes = 36,
-       .eccpos = {
-               6,  7,  8,  9,  10, 11, 12, 13,
-               14, 15, 16, 17, 18, 19, 20, 21,
-               22, 23, 24, 25, 26, 27, 28, 29,
-               30, 31, 32, 33, 34, 35, 36, 37,
-               38, 39, 40, 41
-       },
-       .oobfree = {
-               { .offset = 2, .length = 4 },
-               { .offset = 42, .length = 22 }
-       },
-};
 
 /* Early prototypes of the QI LB60 had only 1GB of NAND.
  * In order to support these devices as well the partition and ecc layout is
@@ -84,25 +70,6 @@ static struct mtd_partition qi_lb60_partitions_1gb[] = {
        },
 };
 
-static struct nand_ecclayout qi_lb60_ecclayout_2gb = {
-       .eccbytes = 72,
-       .eccpos = {
-               12, 13, 14, 15, 16, 17, 18, 19,
-               20, 21, 22, 23, 24, 25, 26, 27,
-               28, 29, 30, 31, 32, 33, 34, 35,
-               36, 37, 38, 39, 40, 41, 42, 43,
-               44, 45, 46, 47, 48, 49, 50, 51,
-               52, 53, 54, 55, 56, 57, 58, 59,
-               60, 61, 62, 63, 64, 65, 66, 67,
-               68, 69, 70, 71, 72, 73, 74, 75,
-               76, 77, 78, 79, 80, 81, 82, 83
-       },
-       .oobfree = {
-               { .offset = 2, .length = 10 },
-               { .offset = 84, .length = 44 },
-       },
-};
-
 static struct mtd_partition qi_lb60_partitions_2gb[] = {
        {
                .name = "NAND BOOT partition",
@@ -121,19 +88,67 @@ static struct mtd_partition qi_lb60_partitions_2gb[] = {
        },
 };
 
+static int qi_lb60_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->length = 36;
+       oobregion->offset = 6;
+
+       if (mtd->oobsize == 128) {
+               oobregion->length *= 2;
+               oobregion->offset *= 2;
+       }
+
+       return 0;
+}
+
+static int qi_lb60_ooblayout_free(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       int eccbytes = 36, eccoff = 6;
+
+       if (section > 1)
+               return -ERANGE;
+
+       if (mtd->oobsize == 128) {
+               eccbytes *= 2;
+               eccoff *= 2;
+       }
+
+       if (!section) {
+               oobregion->offset = 2;
+               oobregion->length = eccoff - 2;
+       } else {
+               oobregion->offset = eccoff + eccbytes;
+               oobregion->length = mtd->oobsize - oobregion->offset;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops qi_lb60_ooblayout_ops = {
+       .ecc = qi_lb60_ooblayout_ecc,
+       .free = qi_lb60_ooblayout_free,
+};
+
 static void qi_lb60_nand_ident(struct platform_device *pdev,
-               struct nand_chip *chip, struct mtd_partition **partitions,
+               struct mtd_info *mtd, struct mtd_partition **partitions,
                int *num_partitions)
 {
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
        if (chip->page_shift == 12) {
-               chip->ecc.layout = &qi_lb60_ecclayout_2gb;
                *partitions = qi_lb60_partitions_2gb;
                *num_partitions = ARRAY_SIZE(qi_lb60_partitions_2gb);
        } else {
-               chip->ecc.layout = &qi_lb60_ecclayout_1gb;
                *partitions = qi_lb60_partitions_1gb;
                *num_partitions = ARRAY_SIZE(qi_lb60_partitions_1gb);
        }
+
+       mtd_set_ooblayout(mtd, &qi_lb60_ooblayout_ops);
 }
 
 static struct jz_nand_platform_data qi_lb60_nand_pdata = {
index 04d706c..35b13a0 100644 (file)
@@ -146,7 +146,6 @@ int bcma_sflash_init(struct bcma_drv_cc *cc)
                return -ENOTSUPP;
        }
 
-       sflash->window = BCMA_SOC_FLASH2;
        sflash->blocksize = e->blocksize;
        sflash->numblocks = e->numblocks;
        sflash->size = sflash->blocksize * sflash->numblocks;
index c61a284..81ddb17 100644 (file)
@@ -51,6 +51,7 @@ config TI_EMIF
 
 config OMAP_GPMC
        bool
+       select GPIOLIB
        help
          This driver is for the General Purpose Memory Controller (GPMC)
          present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows
index 2a691da..904b4af 100644 (file)
@@ -59,11 +59,11 @@ int fsl_ifc_find(phys_addr_t addr_base)
 {
        int i = 0;
 
-       if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs)
+       if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs)
                return -ENODEV;
 
        for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) {
-               u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr);
+               u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr);
                if (cspr & CSPR_V && (cspr & CSPR_BA) ==
                                convert_ifc_address(addr_base))
                        return i;
@@ -75,7 +75,7 @@ EXPORT_SYMBOL(fsl_ifc_find);
 
 static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl)
 {
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
 
        /*
         * Clear all the common status and event registers
@@ -104,7 +104,7 @@ static int fsl_ifc_ctrl_remove(struct platform_device *dev)
        irq_dispose_mapping(ctrl->nand_irq);
        irq_dispose_mapping(ctrl->irq);
 
-       iounmap(ctrl->regs);
+       iounmap(ctrl->gregs);
 
        dev_set_drvdata(&dev->dev, NULL);
        kfree(ctrl);
@@ -122,7 +122,7 @@ static DEFINE_SPINLOCK(nand_irq_lock);
 
 static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl)
 {
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
        unsigned long flags;
        u32 stat;
 
@@ -157,7 +157,7 @@ static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data)
 static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
 {
        struct fsl_ifc_ctrl *ctrl = data;
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
        u32 err_axiid, err_srcid, status, cs_err, err_addr;
        irqreturn_t ret = IRQ_NONE;
 
@@ -215,6 +215,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
 {
        int ret = 0;
        int version, banks;
+       void __iomem *addr;
 
        dev_info(&dev->dev, "Freescale Integrated Flash Controller\n");
 
@@ -225,22 +226,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
        dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev);
 
        /* IOMAP the entire IFC region */
-       fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
-       if (!fsl_ifc_ctrl_dev->regs) {
+       fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0);
+       if (!fsl_ifc_ctrl_dev->gregs) {
                dev_err(&dev->dev, "failed to get memory region\n");
                ret = -ENODEV;
                goto err;
        }
 
-       version = ifc_in32(&fsl_ifc_ctrl_dev->regs->ifc_rev) &
-                       FSL_IFC_VERSION_MASK;
-       banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
-       dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
-               version >> 24, (version >> 16) & 0xf, banks);
-
-       fsl_ifc_ctrl_dev->version = version;
-       fsl_ifc_ctrl_dev->banks = banks;
-
        if (of_property_read_bool(dev->dev.of_node, "little-endian")) {
                fsl_ifc_ctrl_dev->little_endian = true;
                dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n");
@@ -249,8 +241,9 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
                dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n");
        }
 
-       version = ioread32be(&fsl_ifc_ctrl_dev->regs->ifc_rev) &
+       version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) &
                        FSL_IFC_VERSION_MASK;
+
        banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
        dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
                version >> 24, (version >> 16) & 0xf, banks);
@@ -258,6 +251,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
        fsl_ifc_ctrl_dev->version = version;
        fsl_ifc_ctrl_dev->banks = banks;
 
+       addr = fsl_ifc_ctrl_dev->gregs;
+       if (version >= FSL_IFC_VERSION_2_0_0)
+               addr += PGOFFSET_64K;
+       else
+               addr += PGOFFSET_4K;
+       fsl_ifc_ctrl_dev->rregs = addr;
+
        /* get the Controller level irq */
        fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
        if (fsl_ifc_ctrl_dev->irq == 0) {
index 21825dd..af4884b 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/gpio/driver.h>
 #include <linux/interrupt.h>
+#include <linux/irqdomain.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
-#include <linux/of_mtd.h>
 #include <linux/of_device.h>
 #include <linux/of_platform.h>
 #include <linux/omap-gpmc.h>
-#include <linux/mtd/nand.h>
 #include <linux/pm_runtime.h>
 
 #include <linux/platform_data/mtd-nand-omap2.h>
@@ -81,6 +81,8 @@
 
 #define GPMC_CONFIG_LIMITEDADDRESS             BIT(1)
 
+#define GPMC_STATUS_EMPTYWRITEBUFFERSTATUS     BIT(0)
+
 #define        GPMC_CONFIG2_CSEXTRADELAY               BIT(7)
 #define        GPMC_CONFIG3_ADVEXTRADELAY              BIT(7)
 #define        GPMC_CONFIG4_OEEXTRADELAY               BIT(7)
 #define GPMC_CS_SIZE           0x30
 #define        GPMC_BCH_SIZE           0x10
 
+/*
+ * The first 1MB of GPMC address space is typically mapped to
+ * the internal ROM. Never allocate the first page, to
+ * facilitate bug detection; even if we didn't boot from ROM.
+ * As GPMC minimum partition size is 16MB we can only start from
+ * there.
+ */
+#define GPMC_MEM_START         0x1000000
 #define GPMC_MEM_END           0x3FFFFFFF
 
 #define GPMC_CHUNK_SHIFT       24              /* 16 MB */
 #define GPMC_CONFIG_RDY_BSY    0x00000001
 #define GPMC_CONFIG_DEV_SIZE   0x00000002
 #define GPMC_CONFIG_DEV_TYPE   0x00000003
-#define GPMC_SET_IRQ_STATUS    0x00000004
 
 #define GPMC_CONFIG1_WRAPBURST_SUPP     (1 << 31)
 #define GPMC_CONFIG1_READMULTIPLE_SUPP  (1 << 30)
 #define GPMC_CONFIG_WRITEPROTECT       0x00000010
 #define WR_RD_PIN_MONITORING           0x00600000
 
-#define GPMC_ENABLE_IRQ                0x0000000d
-
 /* ECC commands */
 #define GPMC_ECC_READ          0 /* Reset Hardware ECC for read */
 #define GPMC_ECC_WRITE         1 /* Reset Hardware ECC for write */
 #define GPMC_ECC_READSYN       2 /* Reset before syndrom is read back */
 
-/* XXX: Only NAND irq has been considered,currently these are the only ones used
- */
-#define        GPMC_NR_IRQ             2
+#define        GPMC_NR_NAND_IRQS       2 /* number of NAND specific IRQs */
 
 enum gpmc_clk_domain {
        GPMC_CD_FCLK,
@@ -199,11 +204,6 @@ struct gpmc_cs_data {
        struct resource mem;
 };
 
-struct gpmc_client_irq {
-       unsigned                irq;
-       u32                     bitmask;
-};
-
 /* Structure to save gpmc cs context */
 struct gpmc_cs_config {
        u32 config1;
@@ -231,9 +231,15 @@ struct omap3_gpmc_regs {
        struct gpmc_cs_config cs_context[GPMC_CS_NUM];
 };
 
-static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
-static struct irq_chip gpmc_irq_chip;
-static int gpmc_irq_start;
+struct gpmc_device {
+       struct device *dev;
+       int irq;
+       struct irq_chip irq_chip;
+       struct gpio_chip gpio_chip;
+       int nirqs;
+};
+
+static struct irq_domain *gpmc_irq_domain;
 
 static struct resource gpmc_mem_root;
 static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM];
@@ -241,8 +247,6 @@ static DEFINE_SPINLOCK(gpmc_mem_lock);
 /* Define chip-selects as reserved by default until probe completes */
 static unsigned int gpmc_cs_num = GPMC_CS_NUM;
 static unsigned int gpmc_nr_waitpins;
-static struct device *gpmc_dev;
-static int gpmc_irq;
 static resource_size_t phys_base, mem_size;
 static unsigned gpmc_capability;
 static void __iomem *gpmc_base;
@@ -1054,14 +1058,6 @@ int gpmc_configure(int cmd, int wval)
        u32 regval;
 
        switch (cmd) {
-       case GPMC_ENABLE_IRQ:
-               gpmc_write_reg(GPMC_IRQENABLE, wval);
-               break;
-
-       case GPMC_SET_IRQ_STATUS:
-               gpmc_write_reg(GPMC_IRQSTATUS, wval);
-               break;
-
        case GPMC_CONFIG_WP:
                regval = gpmc_read_reg(GPMC_CONFIG);
                if (wval)
@@ -1084,7 +1080,7 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
 {
        int i;
 
-       reg->gpmc_status = gpmc_base + GPMC_STATUS;
+       reg->gpmc_status = NULL;        /* deprecated */
        reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
                                GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
        reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET +
@@ -1118,87 +1114,201 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
        }
 }
 
-int gpmc_get_client_irq(unsigned irq_config)
+static bool gpmc_nand_writebuffer_empty(void)
 {
-       int i;
+       if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS)
+               return true;
 
-       if (hweight32(irq_config) > 1)
+       return false;
+}
+
+static struct gpmc_nand_ops nand_ops = {
+       .nand_writebuffer_empty = gpmc_nand_writebuffer_empty,
+};
+
+/**
+ * gpmc_omap_get_nand_ops - Get the GPMC NAND interface
+ * @regs: the GPMC NAND register map exclusive for NAND use.
+ * @cs: GPMC chip select number on which the NAND sits. The
+ *      register map returned will be specific to this chip select.
+ *
+ * Returns NULL on error e.g. invalid cs.
+ */
+struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs)
+{
+       if (cs >= gpmc_cs_num)
+               return NULL;
+
+       gpmc_update_nand_reg(reg, cs);
+
+       return &nand_ops;
+}
+EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops);
+
+int gpmc_get_client_irq(unsigned irq_config)
+{
+       if (!gpmc_irq_domain) {
+               pr_warn("%s called before GPMC IRQ domain available\n",
+                       __func__);
                return 0;
+       }
 
-       for (i = 0; i < GPMC_NR_IRQ; i++)
-               if (gpmc_client_irq[i].bitmask & irq_config)
-                       return gpmc_client_irq[i].irq;
+       /* we restrict this to NAND IRQs only */
+       if (irq_config >= GPMC_NR_NAND_IRQS)
+               return 0;
 
-       return 0;
+       return irq_create_mapping(gpmc_irq_domain, irq_config);
 }
 
-static int gpmc_irq_endis(unsigned irq, bool endis)
+static int gpmc_irq_endis(unsigned long hwirq, bool endis)
 {
-       int i;
        u32 regval;
 
-       for (i = 0; i < GPMC_NR_IRQ; i++)
-               if (irq == gpmc_client_irq[i].irq) {
-                       regval = gpmc_read_reg(GPMC_IRQENABLE);
-                       if (endis)
-                               regval |= gpmc_client_irq[i].bitmask;
-                       else
-                               regval &= ~gpmc_client_irq[i].bitmask;
-                       gpmc_write_reg(GPMC_IRQENABLE, regval);
-                       break;
-               }
+       /* bits GPMC_NR_NAND_IRQS to 8 are reserved */
+       if (hwirq >= GPMC_NR_NAND_IRQS)
+               hwirq += 8 - GPMC_NR_NAND_IRQS;
+
+       regval = gpmc_read_reg(GPMC_IRQENABLE);
+       if (endis)
+               regval |= BIT(hwirq);
+       else
+               regval &= ~BIT(hwirq);
+       gpmc_write_reg(GPMC_IRQENABLE, regval);
 
        return 0;
 }
 
 static void gpmc_irq_disable(struct irq_data *p)
 {
-       gpmc_irq_endis(p->irq, false);
+       gpmc_irq_endis(p->hwirq, false);
 }
 
 static void gpmc_irq_enable(struct irq_data *p)
 {
-       gpmc_irq_endis(p->irq, true);
+       gpmc_irq_endis(p->hwirq, true);
 }
 
-static void gpmc_irq_noop(struct irq_data *data) { }
+static void gpmc_irq_mask(struct irq_data *d)
+{
+       gpmc_irq_endis(d->hwirq, false);
+}
 
-static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; }
+static void gpmc_irq_unmask(struct irq_data *d)
+{
+       gpmc_irq_endis(d->hwirq, true);
+}
 
-static int gpmc_setup_irq(void)
+static void gpmc_irq_edge_config(unsigned long hwirq, bool rising_edge)
 {
-       int i;
        u32 regval;
 
-       if (!gpmc_irq)
+       /* NAND IRQs polarity is not configurable */
+       if (hwirq < GPMC_NR_NAND_IRQS)
+               return;
+
+       /* WAITPIN starts at BIT 8 */
+       hwirq += 8 - GPMC_NR_NAND_IRQS;
+
+       regval = gpmc_read_reg(GPMC_CONFIG);
+       if (rising_edge)
+               regval &= ~BIT(hwirq);
+       else
+               regval |= BIT(hwirq);
+
+       gpmc_write_reg(GPMC_CONFIG, regval);
+}
+
+static void gpmc_irq_ack(struct irq_data *d)
+{
+       unsigned int hwirq = d->hwirq;
+
+       /* skip reserved bits */
+       if (hwirq >= GPMC_NR_NAND_IRQS)
+               hwirq += 8 - GPMC_NR_NAND_IRQS;
+
+       /* Setting bit to 1 clears (or Acks) the interrupt */
+       gpmc_write_reg(GPMC_IRQSTATUS, BIT(hwirq));
+}
+
+static int gpmc_irq_set_type(struct irq_data *d, unsigned int trigger)
+{
+       /* can't set type for NAND IRQs */
+       if (d->hwirq < GPMC_NR_NAND_IRQS)
                return -EINVAL;
 
-       gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0);
-       if (gpmc_irq_start < 0) {
-               pr_err("irq_alloc_descs failed\n");
-               return gpmc_irq_start;
+       /* We can support either rising or falling edge at a time */
+       if (trigger == IRQ_TYPE_EDGE_FALLING)
+               gpmc_irq_edge_config(d->hwirq, false);
+       else if (trigger == IRQ_TYPE_EDGE_RISING)
+               gpmc_irq_edge_config(d->hwirq, true);
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int gpmc_irq_map(struct irq_domain *d, unsigned int virq,
+                       irq_hw_number_t hw)
+{
+       struct gpmc_device *gpmc = d->host_data;
+
+       irq_set_chip_data(virq, gpmc);
+       if (hw < GPMC_NR_NAND_IRQS) {
+               irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOAUTOEN);
+               irq_set_chip_and_handler(virq, &gpmc->irq_chip,
+                                        handle_simple_irq);
+       } else {
+               irq_set_chip_and_handler(virq, &gpmc->irq_chip,
+                                        handle_edge_irq);
        }
 
-       gpmc_irq_chip.name = "gpmc";
-       gpmc_irq_chip.irq_startup = gpmc_irq_noop_ret;
-       gpmc_irq_chip.irq_enable = gpmc_irq_enable;
-       gpmc_irq_chip.irq_disable = gpmc_irq_disable;
-       gpmc_irq_chip.irq_shutdown = gpmc_irq_noop;
-       gpmc_irq_chip.irq_ack = gpmc_irq_noop;
-       gpmc_irq_chip.irq_mask = gpmc_irq_noop;
-       gpmc_irq_chip.irq_unmask = gpmc_irq_noop;
-
-       gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE;
-       gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT;
-
-       for (i = 0; i < GPMC_NR_IRQ; i++) {
-               gpmc_client_irq[i].irq = gpmc_irq_start + i;
-               irq_set_chip_and_handler(gpmc_client_irq[i].irq,
-                                       &gpmc_irq_chip, handle_simple_irq);
-               irq_modify_status(gpmc_client_irq[i].irq, IRQ_NOREQUEST,
-                                 IRQ_NOAUTOEN);
+       return 0;
+}
+
+static const struct irq_domain_ops gpmc_irq_domain_ops = {
+       .map    = gpmc_irq_map,
+       .xlate  = irq_domain_xlate_twocell,
+};
+
+static irqreturn_t gpmc_handle_irq(int irq, void *data)
+{
+       int hwirq, virq;
+       u32 regval, regvalx;
+       struct gpmc_device *gpmc = data;
+
+       regval = gpmc_read_reg(GPMC_IRQSTATUS);
+       regvalx = regval;
+
+       if (!regval)
+               return IRQ_NONE;
+
+       for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++) {
+               /* skip reserved status bits */
+               if (hwirq == GPMC_NR_NAND_IRQS)
+                       regvalx >>= 8 - GPMC_NR_NAND_IRQS;
+
+               if (regvalx & BIT(hwirq)) {
+                       virq = irq_find_mapping(gpmc_irq_domain, hwirq);
+                       if (!virq) {
+                               dev_warn(gpmc->dev,
+                                        "spurious irq detected hwirq %d, virq %d\n",
+                                        hwirq, virq);
+                       }
+
+                       generic_handle_irq(virq);
+               }
        }
 
+       gpmc_write_reg(GPMC_IRQSTATUS, regval);
+
+       return IRQ_HANDLED;
+}
+
+static int gpmc_setup_irq(struct gpmc_device *gpmc)
+{
+       u32 regval;
+       int rc;
+
        /* Disable interrupts */
        gpmc_write_reg(GPMC_IRQENABLE, 0);
 
@@ -1206,22 +1316,45 @@ static int gpmc_setup_irq(void)
        regval = gpmc_read_reg(GPMC_IRQSTATUS);
        gpmc_write_reg(GPMC_IRQSTATUS, regval);
 
-       return request_irq(gpmc_irq, gpmc_handle_irq, 0, "gpmc", NULL);
+       gpmc->irq_chip.name = "gpmc";
+       gpmc->irq_chip.irq_enable = gpmc_irq_enable;
+       gpmc->irq_chip.irq_disable = gpmc_irq_disable;
+       gpmc->irq_chip.irq_ack = gpmc_irq_ack;
+       gpmc->irq_chip.irq_mask = gpmc_irq_mask;
+       gpmc->irq_chip.irq_unmask = gpmc_irq_unmask;
+       gpmc->irq_chip.irq_set_type = gpmc_irq_set_type;
+
+       gpmc_irq_domain = irq_domain_add_linear(gpmc->dev->of_node,
+                                               gpmc->nirqs,
+                                               &gpmc_irq_domain_ops,
+                                               gpmc);
+       if (!gpmc_irq_domain) {
+               dev_err(gpmc->dev, "IRQ domain add failed\n");
+               return -ENODEV;
+       }
+
+       rc = request_irq(gpmc->irq, gpmc_handle_irq, 0, "gpmc", gpmc);
+       if (rc) {
+               dev_err(gpmc->dev, "failed to request irq %d: %d\n",
+                       gpmc->irq, rc);
+               irq_domain_remove(gpmc_irq_domain);
+               gpmc_irq_domain = NULL;
+       }
+
+       return rc;
 }
 
-static int gpmc_free_irq(void)
+static int gpmc_free_irq(struct gpmc_device *gpmc)
 {
-       int i;
+       int hwirq;
 
-       if (gpmc_irq)
-               free_irq(gpmc_irq, NULL);
+       free_irq(gpmc->irq, gpmc);
 
-       for (i = 0; i < GPMC_NR_IRQ; i++) {
-               irq_set_handler(gpmc_client_irq[i].irq, NULL);
-               irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip);
-       }
+       for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++)
+               irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq));
 
-       irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ);
+       irq_domain_remove(gpmc_irq_domain);
+       gpmc_irq_domain = NULL;
 
        return 0;
 }
@@ -1242,12 +1375,7 @@ static void gpmc_mem_init(void)
 {
        int cs;
 
-       /*
-        * The first 1MB of GPMC address space is typically mapped to
-        * the internal ROM. Never allocate the first page, to
-        * facilitate bug detection; even if we didn't boot from ROM.
-        */
-       gpmc_mem_root.start = SZ_1M;
+       gpmc_mem_root.start = GPMC_MEM_START;
        gpmc_mem_root.end = GPMC_MEM_END;
 
        /* Reserve all regions that has been set up by bootloader */
@@ -1796,105 +1924,6 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
                of_property_read_bool(np, "gpmc,time-para-granularity");
 }
 
-#if IS_ENABLED(CONFIG_MTD_NAND)
-
-static const char * const nand_xfer_types[] = {
-       [NAND_OMAP_PREFETCH_POLLED]             = "prefetch-polled",
-       [NAND_OMAP_POLLED]                      = "polled",
-       [NAND_OMAP_PREFETCH_DMA]                = "prefetch-dma",
-       [NAND_OMAP_PREFETCH_IRQ]                = "prefetch-irq",
-};
-
-static int gpmc_probe_nand_child(struct platform_device *pdev,
-                                struct device_node *child)
-{
-       u32 val;
-       const char *s;
-       struct gpmc_timings gpmc_t;
-       struct omap_nand_platform_data *gpmc_nand_data;
-
-       if (of_property_read_u32(child, "reg", &val) < 0) {
-               dev_err(&pdev->dev, "%s has no 'reg' property\n",
-                       child->full_name);
-               return -ENODEV;
-       }
-
-       gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data),
-                                     GFP_KERNEL);
-       if (!gpmc_nand_data)
-               return -ENOMEM;
-
-       gpmc_nand_data->cs = val;
-       gpmc_nand_data->of_node = child;
-
-       /* Detect availability of ELM module */
-       gpmc_nand_data->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
-       if (gpmc_nand_data->elm_of_node == NULL)
-               gpmc_nand_data->elm_of_node =
-                                       of_parse_phandle(child, "elm_id", 0);
-
-       /* select ecc-scheme for NAND */
-       if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
-               pr_err("%s: ti,nand-ecc-opt not found\n", __func__);
-               return -ENODEV;
-       }
-
-       if (!strcmp(s, "sw"))
-               gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
-       else if (!strcmp(s, "ham1") ||
-                !strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
-               gpmc_nand_data->ecc_opt =
-                               OMAP_ECC_HAM1_CODE_HW;
-       else if (!strcmp(s, "bch4"))
-               if (gpmc_nand_data->elm_of_node)
-                       gpmc_nand_data->ecc_opt =
-                               OMAP_ECC_BCH4_CODE_HW;
-               else
-                       gpmc_nand_data->ecc_opt =
-                               OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
-       else if (!strcmp(s, "bch8"))
-               if (gpmc_nand_data->elm_of_node)
-                       gpmc_nand_data->ecc_opt =
-                               OMAP_ECC_BCH8_CODE_HW;
-               else
-                       gpmc_nand_data->ecc_opt =
-                               OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
-       else if (!strcmp(s, "bch16"))
-               if (gpmc_nand_data->elm_of_node)
-                       gpmc_nand_data->ecc_opt =
-                               OMAP_ECC_BCH16_CODE_HW;
-               else
-                       pr_err("%s: BCH16 requires ELM support\n", __func__);
-       else
-               pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);
-
-       /* select data transfer mode for NAND controller */
-       if (!of_property_read_string(child, "ti,nand-xfer-type", &s))
-               for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++)
-                       if (!strcasecmp(s, nand_xfer_types[val])) {
-                               gpmc_nand_data->xfer_type = val;
-                               break;
-                       }
-
-       gpmc_nand_data->flash_bbt = of_get_nand_on_flash_bbt(child);
-
-       val = of_get_nand_bus_width(child);
-       if (val == 16)
-               gpmc_nand_data->devsize = NAND_BUSWIDTH_16;
-
-       gpmc_read_timings_dt(child, &gpmc_t);
-       gpmc_nand_init(gpmc_nand_data, &gpmc_t);
-
-       return 0;
-}
-#else
-static int gpmc_probe_nand_child(struct platform_device *pdev,
-                                struct device_node *child)
-{
-       return 0;
-}
-#endif
-
 #if IS_ENABLED(CONFIG_MTD_ONENAND)
 static int gpmc_probe_onenand_child(struct platform_device *pdev,
                                 struct device_node *child)
@@ -1950,6 +1979,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
        const char *name;
        int ret, cs;
        u32 val;
+       struct gpio_desc *waitpin_desc = NULL;
+       struct gpmc_device *gpmc = platform_get_drvdata(pdev);
 
        if (of_property_read_u32(child, "reg", &cs) < 0) {
                dev_err(&pdev->dev, "%s has no 'reg' property\n",
@@ -2010,23 +2041,80 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
        if (ret < 0) {
                dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n",
                        cs, &res.start);
+               if (res.start < GPMC_MEM_START) {
+                       dev_info(&pdev->dev,
+                                "GPMC CS %d start cannot be lesser than 0x%x\n",
+                                cs, GPMC_MEM_START);
+               } else if (res.end > GPMC_MEM_END) {
+                       dev_info(&pdev->dev,
+                                "GPMC CS %d end cannot be greater than 0x%x\n",
+                                cs, GPMC_MEM_END);
+               }
                goto err;
        }
 
-       ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width);
-       if (ret < 0)
-               goto err;
+       if (of_node_cmp(child->name, "nand") == 0) {
+               /* Warn about older DT blobs with no compatible property */
+               if (!of_property_read_bool(child, "compatible")) {
+                       dev_warn(&pdev->dev,
+                                "Incompatible NAND node: missing compatible");
+                       ret = -EINVAL;
+                       goto err;
+               }
+       }
+
+       if (of_device_is_compatible(child, "ti,omap2-nand")) {
+               /* NAND specific setup */
+               val = 8;
+               of_property_read_u32(child, "nand-bus-width", &val);
+               switch (val) {
+               case 8:
+                       gpmc_s.device_width = GPMC_DEVWIDTH_8BIT;
+                       break;
+               case 16:
+                       gpmc_s.device_width = GPMC_DEVWIDTH_16BIT;
+                       break;
+               default:
+                       dev_err(&pdev->dev, "%s: invalid 'nand-bus-width'\n",
+                               child->name);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               /* disable write protect */
+               gpmc_configure(GPMC_CONFIG_WP, 0);
+               gpmc_s.device_nand = true;
+       } else {
+               ret = of_property_read_u32(child, "bank-width",
+                                          &gpmc_s.device_width);
+               if (ret < 0)
+                       goto err;
+       }
+
+       /* Reserve wait pin if it is required and valid */
+       if (gpmc_s.wait_on_read || gpmc_s.wait_on_write) {
+               unsigned int wait_pin = gpmc_s.wait_pin;
+
+               waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip,
+                                                        wait_pin, "WAITPIN");
+               if (IS_ERR(waitpin_desc)) {
+                       dev_err(&pdev->dev, "invalid wait-pin: %d\n", wait_pin);
+                       ret = PTR_ERR(waitpin_desc);
+                       goto err;
+               }
+       }
 
        gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings");
+
        ret = gpmc_cs_program_settings(cs, &gpmc_s);
        if (ret < 0)
-               goto err;
+               goto err_cs;
 
        ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s);
        if (ret) {
                dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n",
                        child->name);
-               goto err;
+               goto err_cs;
        }
 
        /* Clear limited address i.e. enable A26-A11 */
@@ -2057,16 +2145,81 @@ err_child_fail:
        dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name);
        ret = -ENODEV;
 
+err_cs:
+       if (waitpin_desc)
+               gpiochip_free_own_desc(waitpin_desc);
+
 err:
        gpmc_cs_free(cs);
 
        return ret;
 }
 
+static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+       return 1;       /* we're input only */
+}
+
+static int gpmc_gpio_direction_input(struct gpio_chip *chip,
+                                    unsigned int offset)
+{
+       return 0;       /* we're input only */
+}
+
+static int gpmc_gpio_direction_output(struct gpio_chip *chip,
+                                     unsigned int offset, int value)
+{
+       return -EINVAL; /* we're input only */
+}
+
+static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
+                         int value)
+{
+}
+
+static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+       u32 reg;
+
+       offset += 8;
+
+       reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset);
+
+       return !!reg;
+}
+
+static int gpmc_gpio_init(struct gpmc_device *gpmc)
+{
+       int ret;
+
+       gpmc->gpio_chip.parent = gpmc->dev;
+       gpmc->gpio_chip.owner = THIS_MODULE;
+       gpmc->gpio_chip.label = DEVICE_NAME;
+       gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
+       gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
+       gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
+       gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
+       gpmc->gpio_chip.set = gpmc_gpio_set;
+       gpmc->gpio_chip.get = gpmc_gpio_get;
+       gpmc->gpio_chip.base = -1;
+
+       ret = gpiochip_add(&gpmc->gpio_chip);
+       if (ret < 0) {
+               dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void gpmc_gpio_exit(struct gpmc_device *gpmc)
+{
+       gpiochip_remove(&gpmc->gpio_chip);
+}
+
 static int gpmc_probe_dt(struct platform_device *pdev)
 {
        int ret;
-       struct device_node *child;
        const struct of_device_id *of_id =
                of_match_device(gpmc_dt_ids, &pdev->dev);
 
@@ -2094,17 +2247,26 @@ static int gpmc_probe_dt(struct platform_device *pdev)
                return ret;
        }
 
+       return 0;
+}
+
+static int gpmc_probe_dt_children(struct platform_device *pdev)
+{
+       int ret;
+       struct device_node *child;
+
        for_each_available_child_of_node(pdev->dev.of_node, child) {
 
                if (!child->name)
                        continue;
 
-               if (of_node_cmp(child->name, "nand") == 0)
-                       ret = gpmc_probe_nand_child(pdev, child);
-               else if (of_node_cmp(child->name, "onenand") == 0)
+               if (of_node_cmp(child->name, "onenand") == 0)
                        ret = gpmc_probe_onenand_child(pdev, child);
                else
                        ret = gpmc_probe_generic_child(pdev, child);
+
+               if (ret)
+                       return ret;
        }
 
        return 0;
@@ -2114,6 +2276,11 @@ static int gpmc_probe_dt(struct platform_device *pdev)
 {
        return 0;
 }
+
+static int gpmc_probe_dt_children(struct platform_device *pdev)
+{
+       return 0;
+}
 #endif
 
 static int gpmc_probe(struct platform_device *pdev)
@@ -2121,6 +2288,14 @@ static int gpmc_probe(struct platform_device *pdev)
        int rc;
        u32 l;
        struct resource *res;
+       struct gpmc_device *gpmc;
+
+       gpmc = devm_kzalloc(&pdev->dev, sizeof(*gpmc), GFP_KERNEL);
+       if (!gpmc)
+               return -ENOMEM;
+
+       gpmc->dev = &pdev->dev;
+       platform_set_drvdata(pdev, gpmc);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL)
@@ -2134,15 +2309,16 @@ static int gpmc_probe(struct platform_device *pdev)
                return PTR_ERR(gpmc_base);
 
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (res == NULL)
-               dev_warn(&pdev->dev, "Failed to get resource: irq\n");
-       else
-               gpmc_irq = res->start;
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get resource: irq\n");
+               return -ENOENT;
+       }
+
+       gpmc->irq = res->start;
 
        gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
        if (IS_ERR(gpmc_l3_clk)) {
                dev_err(&pdev->dev, "Failed to get GPMC fck\n");
-               gpmc_irq = 0;
                return PTR_ERR(gpmc_l3_clk);
        }
 
@@ -2151,11 +2327,18 @@ static int gpmc_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+       if (pdev->dev.of_node) {
+               rc = gpmc_probe_dt(pdev);
+               if (rc)
+                       return rc;
+       } else {
+               gpmc_cs_num = GPMC_CS_NUM;
+               gpmc_nr_waitpins = GPMC_NR_WAITPINS;
+       }
+
        pm_runtime_enable(&pdev->dev);
        pm_runtime_get_sync(&pdev->dev);
 
-       gpmc_dev = &pdev->dev;
-
        l = gpmc_read_reg(GPMC_REVISION);
 
        /*
@@ -2174,36 +2357,51 @@ static int gpmc_probe(struct platform_device *pdev)
                gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS;
        if (GPMC_REVISION_MAJOR(l) > 0x5)
                gpmc_capability |= GPMC_HAS_MUX_AAD;
-       dev_info(gpmc_dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
+       dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
                 GPMC_REVISION_MINOR(l));
 
        gpmc_mem_init();
-
-       if (gpmc_setup_irq() < 0)
-               dev_warn(gpmc_dev, "gpmc_setup_irq failed\n");
-
-       if (!pdev->dev.of_node) {
-               gpmc_cs_num      = GPMC_CS_NUM;
-               gpmc_nr_waitpins = GPMC_NR_WAITPINS;
+       rc = gpmc_gpio_init(gpmc);
+       if (rc)
+               goto gpio_init_failed;
+
+       gpmc->nirqs = GPMC_NR_NAND_IRQS + gpmc_nr_waitpins;
+       rc = gpmc_setup_irq(gpmc);
+       if (rc) {
+               dev_err(gpmc->dev, "gpmc_setup_irq failed\n");
+               goto setup_irq_failed;
        }
 
-       rc = gpmc_probe_dt(pdev);
+       rc = gpmc_probe_dt_children(pdev);
        if (rc < 0) {
-               pm_runtime_put_sync(&pdev->dev);
-               dev_err(gpmc_dev, "failed to probe DT parameters\n");
-               return rc;
+               dev_err(gpmc->dev, "failed to probe DT children\n");
+               goto dt_children_failed;
        }
 
        return 0;
+
+dt_children_failed:
+       gpmc_free_irq(gpmc);
+setup_irq_failed:
+       gpmc_gpio_exit(gpmc);
+gpio_init_failed:
+       gpmc_mem_exit();
+       pm_runtime_put_sync(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
+       return rc;
 }
 
 static int gpmc_remove(struct platform_device *pdev)
 {
-       gpmc_free_irq();
+       struct gpmc_device *gpmc = platform_get_drvdata(pdev);
+
+       gpmc_free_irq(gpmc);
+       gpmc_gpio_exit(gpmc);
        gpmc_mem_exit();
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
-       gpmc_dev = NULL;
+
        return 0;
 }
 
@@ -2249,25 +2447,6 @@ static __exit void gpmc_exit(void)
 postcore_initcall(gpmc_init);
 module_exit(gpmc_exit);
 
-static irqreturn_t gpmc_handle_irq(int irq, void *dev)
-{
-       int i;
-       u32 regval;
-
-       regval = gpmc_read_reg(GPMC_IRQSTATUS);
-
-       if (!regval)
-               return IRQ_NONE;
-
-       for (i = 0; i < GPMC_NR_IRQ; i++)
-               if (regval & gpmc_client_irq[i].bitmask)
-                       generic_handle_irq(gpmc_client_irq[i].irq);
-
-       gpmc_write_reg(GPMC_IRQSTATUS, regval);
-
-       return IRQ_HANDLED;
-}
-
 static struct omap3_gpmc_regs gpmc_context;
 
 void omap3_gpmc_save_context(void)
index 3b3dabc..bbfa1f1 100644 (file)
@@ -115,6 +115,7 @@ config MTD_MAP_BANK_WIDTH_16
 
 config MTD_MAP_BANK_WIDTH_32
        bool "Support 256-bit buswidth" if MTD_CFI_GEOMETRY
+       select MTD_COMPLEX_MAPPINGS if HAS_IOMEM
        default n
        help
          If you wish to support CFI devices on a physical bus which is
index 347bb83..1c65c15 100644 (file)
@@ -2,6 +2,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/ioport.h>
 #include <linux/mtd/mtd.h>
 #include <linux/platform_device.h>
 #include <linux/bcma/bcma.h>
@@ -109,8 +110,7 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
        if ((from + len) > mtd->size)
                return -EINVAL;
 
-       memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from),
-                     len);
+       memcpy_fromio(buf, b47s->window + from, len);
        *retlen = len;
 
        return len;
@@ -275,15 +275,33 @@ static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset,
 
 static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
 {
-       struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
+       struct device *dev = &pdev->dev;
+       struct bcma_sflash *sflash = dev_get_platdata(dev);
        struct bcm47xxsflash *b47s;
+       struct resource *res;
        int err;
 
-       b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL);
+       b47s = devm_kzalloc(dev, sizeof(*b47s), GFP_KERNEL);
        if (!b47s)
                return -ENOMEM;
        sflash->priv = b47s;
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "invalid resource\n");
+               return -EINVAL;
+       }
+       if (!devm_request_mem_region(dev, res->start, resource_size(res),
+                                    res->name)) {
+               dev_err(dev, "can't request region for resource %pR\n", res);
+               return -EBUSY;
+       }
+       b47s->window = ioremap_cache(res->start, resource_size(res));
+       if (!b47s->window) {
+               dev_err(dev, "ioremap failed for resource %pR\n", res);
+               return -ENOMEM;
+       }
+
        b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash);
        b47s->cc_read = bcm47xxsflash_bcma_cc_read;
        b47s->cc_write = bcm47xxsflash_bcma_cc_write;
@@ -297,7 +315,6 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
                break;
        }
 
-       b47s->window = sflash->window;
        b47s->blocksize = sflash->blocksize;
        b47s->numblocks = sflash->numblocks;
        b47s->size = sflash->size;
@@ -306,6 +323,7 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
        err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
        if (err) {
                pr_err("Failed to register MTD device: %d\n", err);
+               iounmap(b47s->window);
                return err;
        }
 
@@ -321,6 +339,7 @@ static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
        struct bcm47xxsflash *b47s = sflash->priv;
 
        mtd_device_unregister(&b47s->mtd);
+       iounmap(b47s->window);
 
        return 0;
 }
index fe93daf..1564b62 100644 (file)
@@ -65,7 +65,8 @@ struct bcm47xxsflash {
 
        enum bcm47xxsflash_type type;
 
-       u32 window;
+       void __iomem *window;
+
        u32 blocksize;
        u16 numblocks;
        u32 size;
index e7b2e43..b833e6c 100644 (file)
@@ -67,16 +67,40 @@ module_param(reliable_mode, uint, 0);
 MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, "
                 "2=reliable) : MLC normal operations are in normal mode");
 
-/**
- * struct docg3_oobinfo - DiskOnChip G3 OOB layout
- * @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC)
- * @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC)
- * @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15
- */
-static struct nand_ecclayout docg3_oobinfo = {
-       .eccbytes = 8,
-       .eccpos = {7, 8, 9, 10, 11, 12, 13, 14},
-       .oobfree = {{0, 7}, {15, 1} },
+static int docg3_ooblayout_ecc(struct mtd_info *mtd, int section,
+                              struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       /* byte 7 is Hamming ECC, byte 8-14 are BCH ECC */
+       oobregion->offset = 7;
+       oobregion->length = 8;
+
+       return 0;
+}
+
+static int docg3_ooblayout_free(struct mtd_info *mtd, int section,
+                               struct mtd_oob_region *oobregion)
+{
+       if (section > 1)
+               return -ERANGE;
+
+       /* free bytes: byte 0 until byte 6, byte 15 */
+       if (!section) {
+               oobregion->offset = 0;
+               oobregion->length = 7;
+       } else {
+               oobregion->offset = 15;
+               oobregion->length = 1;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops nand_ooblayout_docg3_ops = {
+       .ecc = docg3_ooblayout_ecc,
+       .free = docg3_ooblayout_free,
 };
 
 static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
@@ -1857,7 +1881,7 @@ static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
        mtd->_read_oob = doc_read_oob;
        mtd->_write_oob = doc_write_oob;
        mtd->_block_isbad = doc_block_isbad;
-       mtd->ecclayout = &docg3_oobinfo;
+       mtd_set_ooblayout(mtd, &nand_ooblayout_docg3_ops);
        mtd->oobavail = 8;
        mtd->ecc_strength = DOC_ECC_BCH_T;
 
index c9c3b7f..9d68544 100644 (file)
@@ -131,6 +131,28 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
        /* convert the dummy cycles to the number of bytes */
        dummy /= 8;
 
+       if (spi_flash_read_supported(spi)) {
+               struct spi_flash_read_message msg;
+               int ret;
+
+               memset(&msg, 0, sizeof(msg));
+
+               msg.buf = buf;
+               msg.from = from;
+               msg.len = len;
+               msg.read_opcode = nor->read_opcode;
+               msg.addr_width = nor->addr_width;
+               msg.dummy_bytes = dummy;
+               /* TODO: Support other combinations */
+               msg.opcode_nbits = SPI_NBITS_SINGLE;
+               msg.addr_nbits = SPI_NBITS_SINGLE;
+               msg.data_nbits = m25p80_rx_nbits(nor);
+
+               ret = spi_flash_read(spi, &msg);
+               *retlen = msg.retlen;
+               return ret;
+       }
+
        spi_message_init(&m);
        memset(t, 0, (sizeof t));
 
index 708b7e8..220f920 100644 (file)
@@ -353,7 +353,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
  * mechanism
  * returns the size of the memory region found.
  */
-static int fixup_pmc551(struct pci_dev *dev)
+static int __init fixup_pmc551(struct pci_dev *dev)
 {
 #ifdef CONFIG_MTD_PMC551_BUGFIX
        u32 dram_data;
index 0455166..4f206a9 100644 (file)
@@ -112,8 +112,8 @@ static void ck804xrom_cleanup(struct ck804xrom_window *window)
 }
 
 
-static int ck804xrom_init_one(struct pci_dev *pdev,
-                             const struct pci_device_id *ent)
+static int __init ck804xrom_init_one(struct pci_dev *pdev,
+                                    const struct pci_device_id *ent)
 {
        static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
        u8 byte;
index 76ed651..9646b07 100644 (file)
@@ -144,8 +144,8 @@ static void esb2rom_cleanup(struct esb2rom_window *window)
        pci_dev_put(window->pdev);
 }
 
-static int esb2rom_init_one(struct pci_dev *pdev,
-                           const struct pci_device_id *ent)
+static int __init esb2rom_init_one(struct pci_dev *pdev,
+                                  const struct pci_device_id *ent)
 {
        static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
        struct esb2rom_window *window = &esb2rom_window;
index 8636bba..e17d02a 100644 (file)
@@ -84,8 +84,8 @@ static void ichxrom_cleanup(struct ichxrom_window *window)
 }
 
 
-static int ichxrom_init_one(struct pci_dev *pdev,
-                           const struct pci_device_id *ent)
+static int __init ichxrom_init_one(struct pci_dev *pdev,
+                                  const struct pci_device_id *ent)
 {
        static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
        struct ichxrom_window *window = &ichxrom_window;
index c1af83d..00a8190 100644 (file)
@@ -4,11 +4,13 @@
  *     uclinux.c -- generic memory mapped MTD driver for uclinux
  *
  *     (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
+ *
+ *      License: GPL
  */
 
 /****************************************************************************/
 
-#include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -117,27 +119,6 @@ static int __init uclinux_mtd_init(void)
 
        return(0);
 }
-
-/****************************************************************************/
-
-static void __exit uclinux_mtd_cleanup(void)
-{
-       if (uclinux_ram_mtdinfo) {
-               mtd_device_unregister(uclinux_ram_mtdinfo);
-               map_destroy(uclinux_ram_mtdinfo);
-               uclinux_ram_mtdinfo = NULL;
-       }
-       if (uclinux_ram_map.virt)
-               uclinux_ram_map.virt = 0;
-}
-
-/****************************************************************************/
-
-module_init(uclinux_mtd_init);
-module_exit(uclinux_mtd_cleanup);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
-MODULE_DESCRIPTION("Generic MTD for uClinux");
+device_initcall(uclinux_mtd_init);
 
 /****************************************************************************/
index 6d19835..2a47a3f 100644 (file)
@@ -465,35 +465,108 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd,
 }
 
 /*
- * Copies (and truncates, if necessary) data from the larger struct,
- * nand_ecclayout, to the smaller, deprecated layout struct,
- * nand_ecclayout_user. This is necessary only to support the deprecated
- * API ioctl ECCGETLAYOUT while allowing all new functionality to use
- * nand_ecclayout flexibly (i.e. the struct may change size in new
- * releases without requiring major rewrites).
+ * Copies (and truncates, if necessary) OOB layout information to the
+ * deprecated layout struct, nand_ecclayout_user. This is necessary only to
+ * support the deprecated API ioctl ECCGETLAYOUT while allowing all new
+ * functionality to use mtd_ooblayout_ops flexibly (i.e. mtd_ooblayout_ops
+ * can describe any kind of OOB layout with almost zero overhead from a
+ * memory usage point of view).
  */
-static int shrink_ecclayout(const struct nand_ecclayout *from,
-               struct nand_ecclayout_user *to)
+static int shrink_ecclayout(struct mtd_info *mtd,
+                           struct nand_ecclayout_user *to)
 {
-       int i;
+       struct mtd_oob_region oobregion;
+       int i, section = 0, ret;
 
-       if (!from || !to)
+       if (!mtd || !to)
                return -EINVAL;
 
        memset(to, 0, sizeof(*to));
 
-       to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
-       for (i = 0; i < to->eccbytes; i++)
-               to->eccpos[i] = from->eccpos[i];
+       to->eccbytes = 0;
+       for (i = 0; i < MTD_MAX_ECCPOS_ENTRIES;) {
+               u32 eccpos;
+
+               ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
+               if (ret < 0) {
+                       if (ret != -ERANGE)
+                               return ret;
+
+                       break;
+               }
+
+               eccpos = oobregion.offset;
+               for (; i < MTD_MAX_ECCPOS_ENTRIES &&
+                      eccpos < oobregion.offset + oobregion.length; i++) {
+                       to->eccpos[i] = eccpos++;
+                       to->eccbytes++;
+               }
+       }
 
        for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
-               if (from->oobfree[i].length == 0 &&
-                               from->oobfree[i].offset == 0)
+               ret = mtd_ooblayout_free(mtd, i, &oobregion);
+               if (ret < 0) {
+                       if (ret != -ERANGE)
+                               return ret;
+
+                       break;
+               }
+
+               to->oobfree[i].offset = oobregion.offset;
+               to->oobfree[i].length = oobregion.length;
+               to->oobavail += to->oobfree[i].length;
+       }
+
+       return 0;
+}
+
+static int get_oobinfo(struct mtd_info *mtd, struct nand_oobinfo *to)
+{
+       struct mtd_oob_region oobregion;
+       int i, section = 0, ret;
+
+       if (!mtd || !to)
+               return -EINVAL;
+
+       memset(to, 0, sizeof(*to));
+
+       to->eccbytes = 0;
+       for (i = 0; i < ARRAY_SIZE(to->eccpos);) {
+               u32 eccpos;
+
+               ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
+               if (ret < 0) {
+                       if (ret != -ERANGE)
+                               return ret;
+
                        break;
-               to->oobavail += from->oobfree[i].length;
-               to->oobfree[i] = from->oobfree[i];
+               }
+
+               if (oobregion.length + i > ARRAY_SIZE(to->eccpos))
+                       return -EINVAL;
+
+               eccpos = oobregion.offset;
+               for (; eccpos < oobregion.offset + oobregion.length; i++) {
+                       to->eccpos[i] = eccpos++;
+                       to->eccbytes++;
+               }
        }
 
+       for (i = 0; i < 8; i++) {
+               ret = mtd_ooblayout_free(mtd, i, &oobregion);
+               if (ret < 0) {
+                       if (ret != -ERANGE)
+                               return ret;
+
+                       break;
+               }
+
+               to->oobfree[i][0] = oobregion.offset;
+               to->oobfree[i][1] = oobregion.length;
+       }
+
+       to->useecc = MTD_NANDECC_AUTOPLACE;
+
        return 0;
 }
 
@@ -815,16 +888,12 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
        {
                struct nand_oobinfo oi;
 
-               if (!mtd->ecclayout)
+               if (!mtd->ooblayout)
                        return -EOPNOTSUPP;
-               if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
-                       return -EINVAL;
 
-               oi.useecc = MTD_NANDECC_AUTOPLACE;
-               memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
-               memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
-                      sizeof(oi.oobfree));
-               oi.eccbytes = mtd->ecclayout->eccbytes;
+               ret = get_oobinfo(mtd, &oi);
+               if (ret)
+                       return ret;
 
                if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
                        return -EFAULT;
@@ -913,14 +982,14 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
        {
                struct nand_ecclayout_user *usrlay;
 
-               if (!mtd->ecclayout)
+               if (!mtd->ooblayout)
                        return -EOPNOTSUPP;
 
                usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
                if (!usrlay)
                        return -ENOMEM;
 
-               shrink_ecclayout(mtd->ecclayout, usrlay);
+               shrink_ecclayout(mtd, usrlay);
 
                if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
                        ret = -EFAULT;
index 239a8c8..d573606 100644 (file)
@@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
 
        }
 
-       concat->mtd.ecclayout = subdev[0]->ecclayout;
+       mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout);
 
        concat->num_subdev = num_devs;
        concat->mtd.name = name;
index bee180b..e3936b8 100644 (file)
@@ -1016,6 +1016,366 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to,
 }
 EXPORT_SYMBOL_GPL(mtd_write_oob);
 
+/**
+ * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section
+ * @mtd: MTD device structure
+ * @section: ECC section. Depending on the layout you may have all the ECC
+ *          bytes stored in a single contiguous section, or one section
+ *          per ECC chunk (and sometime several sections for a single ECC
+ *          ECC chunk)
+ * @oobecc: OOB region struct filled with the appropriate ECC position
+ *         information
+ *
+ * This functions return ECC section information in the OOB area. I you want
+ * to get all the ECC bytes information, then you should call
+ * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
+                     struct mtd_oob_region *oobecc)
+{
+       memset(oobecc, 0, sizeof(*oobecc));
+
+       if (!mtd || section < 0)
+               return -EINVAL;
+
+       if (!mtd->ooblayout || !mtd->ooblayout->ecc)
+               return -ENOTSUPP;
+
+       return mtd->ooblayout->ecc(mtd, section, oobecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
+
+/**
+ * mtd_ooblayout_free - Get the OOB region definition of a specific free
+ *                     section
+ * @mtd: MTD device structure
+ * @section: Free section you are interested in. Depending on the layout
+ *          you may have all the free bytes stored in a single contiguous
+ *          section, or one section per ECC chunk plus an extra section
+ *          for the remaining bytes (or other funky layout).
+ * @oobfree: OOB region struct filled with the appropriate free position
+ *          information
+ *
+ * This functions return free bytes position in the OOB area. I you want
+ * to get all the free bytes information, then you should call
+ * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_free(struct mtd_info *mtd, int section,
+                      struct mtd_oob_region *oobfree)
+{
+       memset(oobfree, 0, sizeof(*oobfree));
+
+       if (!mtd || section < 0)
+               return -EINVAL;
+
+       if (!mtd->ooblayout || !mtd->ooblayout->free)
+               return -ENOTSUPP;
+
+       return mtd->ooblayout->free(mtd, section, oobfree);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
+
+/**
+ * mtd_ooblayout_find_region - Find the region attached to a specific byte
+ * @mtd: mtd info structure
+ * @byte: the byte we are searching for
+ * @sectionp: pointer where the section id will be stored
+ * @oobregion: used to retrieve the ECC position
+ * @iter: iterator function. Should be either mtd_ooblayout_free or
+ *       mtd_ooblayout_ecc depending on the region type you're searching for
+ *
+ * This functions returns the section id and oobregion information of a
+ * specific byte. For example, say you want to know where the 4th ECC byte is
+ * stored, you'll use:
+ *
+ * mtd_ooblayout_find_region(mtd, 3, &section, &oobregion, mtd_ooblayout_ecc);
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte,
+                               int *sectionp, struct mtd_oob_region *oobregion,
+                               int (*iter)(struct mtd_info *,
+                                           int section,
+                                           struct mtd_oob_region *oobregion))
+{
+       int pos = 0, ret, section = 0;
+
+       memset(oobregion, 0, sizeof(*oobregion));
+
+       while (1) {
+               ret = iter(mtd, section, oobregion);
+               if (ret)
+                       return ret;
+
+               if (pos + oobregion->length > byte)
+                       break;
+
+               pos += oobregion->length;
+               section++;
+       }
+
+       /*
+        * Adjust region info to make it start at the beginning at the
+        * 'start' ECC byte.
+        */
+       oobregion->offset += byte - pos;
+       oobregion->length -= byte - pos;
+       *sectionp = section;
+
+       return 0;
+}
+
+/**
+ * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific
+ *                               ECC byte
+ * @mtd: mtd info structure
+ * @eccbyte: the byte we are searching for
+ * @sectionp: pointer where the section id will be stored
+ * @oobregion: OOB region information
+ *
+ * Works like mtd_ooblayout_find_region() except it searches for a specific ECC
+ * byte.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
+                                int *section,
+                                struct mtd_oob_region *oobregion)
+{
+       return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion,
+                                        mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion);
+
+/**
+ * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @buf: destination buffer to store OOB bytes
+ * @oobbuf: OOB buffer
+ * @start: first byte to retrieve
+ * @nbytes: number of bytes to retrieve
+ * @iter: section iterator
+ *
+ * Extract bytes attached to a specific category (ECC or free)
+ * from the OOB buffer and copy them into buf.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
+                               const u8 *oobbuf, int start, int nbytes,
+                               int (*iter)(struct mtd_info *,
+                                           int section,
+                                           struct mtd_oob_region *oobregion))
+{
+       struct mtd_oob_region oobregion = { };
+       int section = 0, ret;
+
+       ret = mtd_ooblayout_find_region(mtd, start, &section,
+                                       &oobregion, iter);
+
+       while (!ret) {
+               int cnt;
+
+               cnt = oobregion.length > nbytes ? nbytes : oobregion.length;
+               memcpy(buf, oobbuf + oobregion.offset, cnt);
+               buf += cnt;
+               nbytes -= cnt;
+
+               if (!nbytes)
+                       break;
+
+               ret = iter(mtd, ++section, &oobregion);
+       }
+
+       return ret;
+}
+
+/**
+ * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @buf: source buffer to get OOB bytes from
+ * @oobbuf: OOB buffer
+ * @start: first OOB byte to set
+ * @nbytes: number of OOB bytes to set
+ * @iter: section iterator
+ *
+ * Fill the OOB buffer with data provided in buf. The category (ECC or free)
+ * is selected by passing the appropriate iterator.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
+                               u8 *oobbuf, int start, int nbytes,
+                               int (*iter)(struct mtd_info *,
+                                           int section,
+                                           struct mtd_oob_region *oobregion))
+{
+       struct mtd_oob_region oobregion = { };
+       int section = 0, ret;
+
+       ret = mtd_ooblayout_find_region(mtd, start, &section,
+                                       &oobregion, iter);
+
+       while (!ret) {
+               int cnt;
+
+               cnt = oobregion.length > nbytes ? nbytes : oobregion.length;
+               memcpy(oobbuf + oobregion.offset, buf, cnt);
+               buf += cnt;
+               nbytes -= cnt;
+
+               if (!nbytes)
+                       break;
+
+               ret = iter(mtd, ++section, &oobregion);
+       }
+
+       return ret;
+}
+
+/**
+ * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category
+ * @mtd: mtd info structure
+ * @iter: category iterator
+ *
+ * Count the number of bytes in a given category.
+ *
+ * Returns a positive value on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_count_bytes(struct mtd_info *mtd,
+                               int (*iter)(struct mtd_info *,
+                                           int section,
+                                           struct mtd_oob_region *oobregion))
+{
+       struct mtd_oob_region oobregion = { };
+       int section = 0, ret, nbytes = 0;
+
+       while (1) {
+               ret = iter(mtd, section++, &oobregion);
+               if (ret) {
+                       if (ret == -ERANGE)
+                               ret = nbytes;
+                       break;
+               }
+
+               nbytes += oobregion.length;
+       }
+
+       return ret;
+}
+
+/**
+ * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: destination buffer to store ECC bytes
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to retrieve
+ * @nbytes: number of ECC bytes to retrieve
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
+                              const u8 *oobbuf, int start, int nbytes)
+{
+       return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes,
+                                      mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes);
+
+/**
+ * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: source buffer to get ECC bytes from
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to set
+ * @nbytes: number of ECC bytes to set
+ *
+ * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
+                              u8 *oobbuf, int start, int nbytes)
+{
+       return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes,
+                                      mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes);
+
+/**
+ * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @databuf: destination buffer to store ECC bytes
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to retrieve
+ * @nbytes: number of ECC bytes to retrieve
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
+                               const u8 *oobbuf, int start, int nbytes)
+{
+       return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes,
+                                      mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
+
+/**
+ * mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: source buffer to get data bytes from
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to set
+ * @nbytes: number of ECC bytes to set
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
+                               u8 *oobbuf, int start, int nbytes)
+{
+       return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes,
+                                      mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes);
+
+/**
+ * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB
+ * @mtd: mtd info structure
+ *
+ * Works like mtd_ooblayout_count_bytes(), except it count free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
+{
+       return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
+
+/**
+ * mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB
+ * @mtd: mtd info structure
+ *
+ * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
+{
+       return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
+
 /*
  * Method to access the protection register area, present in some flash
  * devices. The user data is one time programmable but the factory data is read
index 08de4b2..1f13e32 100644 (file)
@@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
        return res;
 }
 
+static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
+                             struct mtd_oob_region *oobregion)
+{
+       struct mtd_part *part = mtd_to_part(mtd);
+
+       return mtd_ooblayout_ecc(part->master, section, oobregion);
+}
+
+static int part_ooblayout_free(struct mtd_info *mtd, int section,
+                              struct mtd_oob_region *oobregion)
+{
+       struct mtd_part *part = mtd_to_part(mtd);
+
+       return mtd_ooblayout_free(part->master, section, oobregion);
+}
+
+static const struct mtd_ooblayout_ops part_ooblayout_ops = {
+       .ecc = part_ooblayout_ecc,
+       .free = part_ooblayout_free,
+};
+
 static inline void free_partition(struct mtd_part *p)
 {
        kfree(p->mtd.name);
@@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
                        part->name);
        }
 
-       slave->mtd.ecclayout = master->ecclayout;
+       mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
        slave->mtd.ecc_step_size = master->ecc_step_size;
        slave->mtd.ecc_strength = master->ecc_strength;
        slave->mtd.bitflip_threshold = master->bitflip_threshold;
index 68b58c8..78e12cc 100644 (file)
@@ -224,6 +224,7 @@ static int ams_delta_init(struct platform_device *pdev)
        /* 25 us command delay time */
        this->chip_delay = 30;
        this->ecc.mode = NAND_ECC_SOFT;
+       this->ecc.algo = NAND_ECC_HAMMING;
 
        platform_set_drvdata(pdev, io_base);
 
index 20cbaab..efc8ea2 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
-#include <linux/of_mtd.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
@@ -72,30 +71,44 @@ struct atmel_nand_nfc_caps {
        uint32_t rb_mask;
 };
 
-/* oob layout for large page size
+/*
+ * oob layout for large page size
  * bad block info is on bytes 0 and 1
  * the bytes have to be consecutives to avoid
  * several NAND_CMD_RNDOUT during read
- */
-static struct nand_ecclayout atmel_oobinfo_large = {
-       .eccbytes = 4,
-       .eccpos = {60, 61, 62, 63},
-       .oobfree = {
-               {2, 58}
-       },
-};
-
-/* oob layout for small page size
+ *
+ * oob layout for small page size
  * bad block info is on bytes 4 and 5
  * the bytes have to be consecutives to avoid
  * several NAND_CMD_RNDOUT during read
  */
-static struct nand_ecclayout atmel_oobinfo_small = {
-       .eccbytes = 4,
-       .eccpos = {0, 1, 2, 3},
-       .oobfree = {
-               {6, 10}
-       },
+static int atmel_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->length = 4;
+       oobregion->offset = 0;
+
+       return 0;
+}
+
+static int atmel_ooblayout_free_sp(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 6;
+       oobregion->length = mtd->oobsize - oobregion->offset;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops atmel_ooblayout_sp_ops = {
+       .ecc = atmel_ooblayout_ecc_sp,
+       .free = atmel_ooblayout_free_sp,
 };
 
 struct atmel_nfc {
@@ -163,8 +176,6 @@ struct atmel_nand_host {
        int                     *pmecc_delta;
 };
 
-static struct nand_ecclayout atmel_pmecc_oobinfo;
-
 /*
  * Enable NAND.
  */
@@ -434,14 +445,13 @@ err_buf:
 static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
 
        if (use_dma && len > mtd->oobsize)
                /* only use DMA for bigger than oob size: better performances */
                if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
                        return;
 
-       if (host->board.bus_width_16)
+       if (chip->options & NAND_BUSWIDTH_16)
                atmel_read_buf16(mtd, buf, len);
        else
                atmel_read_buf8(mtd, buf, len);
@@ -450,14 +460,13 @@ static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
 static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
 
        if (use_dma && len > mtd->oobsize)
                /* only use DMA for bigger than oob size: better performances */
                if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
                        return;
 
-       if (host->board.bus_width_16)
+       if (chip->options & NAND_BUSWIDTH_16)
                atmel_write_buf16(mtd, buf, len);
        else
                atmel_write_buf8(mtd, buf, len);
@@ -483,22 +492,6 @@ static int pmecc_get_ecc_bytes(int cap, int sector_size)
        return (m * cap + 7) / 8;
 }
 
-static void pmecc_config_ecc_layout(struct nand_ecclayout *layout,
-                                   int oobsize, int ecc_len)
-{
-       int i;
-
-       layout->eccbytes = ecc_len;
-
-       /* ECC will occupy the last ecc_len bytes continuously */
-       for (i = 0; i < ecc_len; i++)
-               layout->eccpos[i] = oobsize - ecc_len + i;
-
-       layout->oobfree[0].offset = PMECC_OOB_RESERVED_BYTES;
-       layout->oobfree[0].length =
-               oobsize - ecc_len - layout->oobfree[0].offset;
-}
-
 static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)
 {
        int table_size;
@@ -836,13 +829,16 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
                        dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
                                pos, bit_pos, err_byte, *(buf + byte_pos));
                } else {
+                       struct mtd_oob_region oobregion;
+
                        /* Bit flip in OOB area */
                        tmp = sector_num * nand_chip->ecc.bytes
                                        + (byte_pos - sector_size);
                        err_byte = ecc[tmp];
                        ecc[tmp] ^= (1 << bit_pos);
 
-                       pos = tmp + nand_chip->ecc.layout->eccpos[0];
+                       mtd_ooblayout_ecc(mtd, 0, &oobregion);
+                       pos = tmp + oobregion.offset;
                        dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
                                pos, bit_pos, err_byte, ecc[tmp]);
                }
@@ -863,17 +859,6 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
        uint8_t *buf_pos;
        int max_bitflips = 0;
 
-       /* If can correct bitfilps from erased page, do the normal check */
-       if (host->caps->pmecc_correct_erase_page)
-               goto normal_check;
-
-       for (i = 0; i < nand_chip->ecc.total; i++)
-               if (ecc[i] != 0xff)
-                       goto normal_check;
-       /* Erased page, return OK */
-       return 0;
-
-normal_check:
        for (i = 0; i < nand_chip->ecc.steps; i++) {
                err_nbr = 0;
                if (pmecc_stat & 0x1) {
@@ -884,16 +869,30 @@ normal_check:
                        pmecc_get_sigma(mtd);
 
                        err_nbr = pmecc_err_location(mtd);
-                       if (err_nbr == -1) {
+                       if (err_nbr >= 0) {
+                               pmecc_correct_data(mtd, buf_pos, ecc, i,
+                                                  nand_chip->ecc.bytes,
+                                                  err_nbr);
+                       } else if (!host->caps->pmecc_correct_erase_page) {
+                               u8 *ecc_pos = ecc + (i * nand_chip->ecc.bytes);
+
+                               /* Try to detect erased pages */
+                               err_nbr = nand_check_erased_ecc_chunk(buf_pos,
+                                                       host->pmecc_sector_size,
+                                                       ecc_pos,
+                                                       nand_chip->ecc.bytes,
+                                                       NULL, 0,
+                                                       nand_chip->ecc.strength);
+                       }
+
+                       if (err_nbr < 0) {
                                dev_err(host->dev, "PMECC: Too many errors\n");
                                mtd->ecc_stats.failed++;
                                return -EIO;
-                       } else {
-                               pmecc_correct_data(mtd, buf_pos, ecc, i,
-                                       nand_chip->ecc.bytes, err_nbr);
-                               mtd->ecc_stats.corrected += err_nbr;
-                               max_bitflips = max_t(int, max_bitflips, err_nbr);
                        }
+
+                       mtd->ecc_stats.corrected += err_nbr;
+                       max_bitflips = max_t(int, max_bitflips, err_nbr);
                }
                pmecc_stat >>= 1;
        }
@@ -931,7 +930,6 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
        struct atmel_nand_host *host = nand_get_controller_data(chip);
        int eccsize = chip->ecc.size * chip->ecc.steps;
        uint8_t *oob = chip->oob_poi;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
        uint32_t stat;
        unsigned long end_time;
        int bitflips = 0;
@@ -953,7 +951,11 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
 
        stat = pmecc_readl_relaxed(host->ecc, ISR);
        if (stat != 0) {
-               bitflips = pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]);
+               struct mtd_oob_region oobregion;
+
+               mtd_ooblayout_ecc(mtd, 0, &oobregion);
+               bitflips = pmecc_correction(mtd, stat, buf,
+                                           &oob[oobregion.offset]);
                if (bitflips < 0)
                        /* uncorrectable errors */
                        return 0;
@@ -967,8 +969,8 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
                int page)
 {
        struct atmel_nand_host *host = nand_get_controller_data(chip);
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
-       int i, j;
+       struct mtd_oob_region oobregion = { };
+       int i, j, section = 0;
        unsigned long end_time;
 
        if (!host->nfc || !host->nfc->write_by_sram) {
@@ -987,11 +989,14 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
 
        for (i = 0; i < chip->ecc.steps; i++) {
                for (j = 0; j < chip->ecc.bytes; j++) {
-                       int pos;
+                       if (!oobregion.length)
+                               mtd_ooblayout_ecc(mtd, section, &oobregion);
 
-                       pos = i * chip->ecc.bytes + j;
-                       chip->oob_poi[eccpos[pos]] =
+                       chip->oob_poi[oobregion.offset] =
                                pmecc_readb_ecc_relaxed(host->ecc, i, j);
+                       oobregion.length--;
+                       oobregion.offset++;
+                       section++;
                }
        }
        chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -1003,8 +1008,9 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
 {
        struct nand_chip *nand_chip = mtd_to_nand(mtd);
        struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
+       int eccbytes = mtd_ooblayout_count_eccbytes(mtd);
        uint32_t val = 0;
-       struct nand_ecclayout *ecc_layout;
+       struct mtd_oob_region oobregion;
 
        pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
        pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
@@ -1054,11 +1060,11 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
                | PMECC_CFG_AUTO_DISABLE);
        pmecc_writel(host->ecc, CFG, val);
 
-       ecc_layout = nand_chip->ecc.layout;
        pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1);
-       pmecc_writel(host->ecc, SADDR, ecc_layout->eccpos[0]);
+       mtd_ooblayout_ecc(mtd, 0, &oobregion);
+       pmecc_writel(host->ecc, SADDR, oobregion.offset);
        pmecc_writel(host->ecc, EADDR,
-                       ecc_layout->eccpos[ecc_layout->eccbytes - 1]);
+                    oobregion.offset + eccbytes - 1);
        /* See datasheet about PMECC Clock Control Register */
        pmecc_writel(host->ecc, CLK, 2);
        pmecc_writel(host->ecc, IDR, 0xff);
@@ -1206,6 +1212,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
                dev_warn(host->dev,
                        "Can't get I/O resource regs for PMECC controller, rolling back on software ECC\n");
                nand_chip->ecc.mode = NAND_ECC_SOFT;
+               nand_chip->ecc.algo = NAND_ECC_HAMMING;
                return 0;
        }
 
@@ -1280,11 +1287,8 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
                        err_no = -EINVAL;
                        goto err;
                }
-               pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
-                                       mtd->oobsize,
-                                       nand_chip->ecc.total);
 
-               nand_chip->ecc.layout = &atmel_pmecc_oobinfo;
+               mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
                break;
        default:
                dev_warn(host->dev,
@@ -1292,6 +1296,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
                /* page size not handled by HW ECC */
                /* switching back to soft ECC */
                nand_chip->ecc.mode = NAND_ECC_SOFT;
+               nand_chip->ecc.algo = NAND_ECC_HAMMING;
                return 0;
        }
 
@@ -1359,12 +1364,12 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 {
        int eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
        uint8_t *p = buf;
        uint8_t *oob = chip->oob_poi;
        uint8_t *ecc_pos;
        int stat;
        unsigned int max_bitflips = 0;
+       struct mtd_oob_region oobregion = {};
 
        /*
         * Errata: ALE is incorrectly wired up to the ECC controller
@@ -1382,19 +1387,20 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
        chip->read_buf(mtd, p, eccsize);
 
        /* move to ECC position if needed */
-       if (eccpos[0] != 0) {
-               /* This only works on large pages
-                * because the ECC controller waits for
-                * NAND_CMD_RNDOUTSTART after the
-                * NAND_CMD_RNDOUT.
-                * anyway, for small pages, the eccpos[0] == 0
+       mtd_ooblayout_ecc(mtd, 0, &oobregion);
+       if (oobregion.offset != 0) {
+               /*
+                * This only works on large pages because the ECC controller
+                * waits for NAND_CMD_RNDOUTSTART after the NAND_CMD_RNDOUT.
+                * Anyway, for small pages, the first ECC byte is at offset
+                * 0 in the OOB area.
                 */
                chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
-                               mtd->writesize + eccpos[0], -1);
+                             mtd->writesize + oobregion.offset, -1);
        }
 
        /* the ECC controller needs to read the ECC just after the data */
-       ecc_pos = oob + eccpos[0];
+       ecc_pos = oob + oobregion.offset;
        chip->read_buf(mtd, ecc_pos, eccbytes);
 
        /* check if there's an error */
@@ -1504,58 +1510,17 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
                ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
 }
 
-static int atmel_of_init_port(struct atmel_nand_host *host,
-                             struct device_node *np)
+static int atmel_of_init_ecc(struct atmel_nand_host *host,
+                            struct device_node *np)
 {
-       u32 val;
        u32 offset[2];
-       int ecc_mode;
-       struct atmel_nand_data *board = &host->board;
-       enum of_gpio_flags flags = 0;
-
-       host->caps = (struct atmel_nand_caps *)
-               of_device_get_match_data(host->dev);
-
-       if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
-               if (val >= 32) {
-                       dev_err(host->dev, "invalid addr-offset %u\n", val);
-                       return -EINVAL;
-               }
-               board->ale = val;
-       }
-
-       if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) {
-               if (val >= 32) {
-                       dev_err(host->dev, "invalid cmd-offset %u\n", val);
-                       return -EINVAL;
-               }
-               board->cle = val;
-       }
-
-       ecc_mode = of_get_nand_ecc_mode(np);
-
-       board->ecc_mode = ecc_mode < 0 ? NAND_ECC_SOFT : ecc_mode;
-
-       board->on_flash_bbt = of_get_nand_on_flash_bbt(np);
-
-       board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma");
-
-       if (of_get_nand_bus_width(np) == 16)
-               board->bus_width_16 = 1;
-
-       board->rdy_pin = of_get_gpio_flags(np, 0, &flags);
-       board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW);
-
-       board->enable_pin = of_get_gpio(np, 1);
-       board->det_pin = of_get_gpio(np, 2);
+       u32 val;
 
        host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc");
 
-       /* load the nfc driver if there is */
-       of_platform_populate(np, NULL, NULL, host->dev);
-
-       if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc)
-               return 0;       /* Not using PMECC */
+       /* Not using PMECC */
+       if (!(host->nand_chip.ecc.mode == NAND_ECC_HW) || !host->has_pmecc)
+               return 0;
 
        /* use PMECC, get correction capability, sector size and lookup
         * table offset.
@@ -1596,16 +1561,65 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
                /* Will build a lookup table and initialize the offset later */
                return 0;
        }
+
        if (!offset[0] && !offset[1]) {
                dev_err(host->dev, "Invalid PMECC lookup table offset\n");
                return -EINVAL;
        }
+
        host->pmecc_lookup_table_offset_512 = offset[0];
        host->pmecc_lookup_table_offset_1024 = offset[1];
 
        return 0;
 }
 
+static int atmel_of_init_port(struct atmel_nand_host *host,
+                             struct device_node *np)
+{
+       u32 val;
+       struct atmel_nand_data *board = &host->board;
+       enum of_gpio_flags flags = 0;
+
+       host->caps = (struct atmel_nand_caps *)
+               of_device_get_match_data(host->dev);
+
+       if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
+               if (val >= 32) {
+                       dev_err(host->dev, "invalid addr-offset %u\n", val);
+                       return -EINVAL;
+               }
+               board->ale = val;
+       }
+
+       if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) {
+               if (val >= 32) {
+                       dev_err(host->dev, "invalid cmd-offset %u\n", val);
+                       return -EINVAL;
+               }
+               board->cle = val;
+       }
+
+       board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma");
+
+       board->rdy_pin = of_get_gpio_flags(np, 0, &flags);
+       board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW);
+
+       board->enable_pin = of_get_gpio(np, 1);
+       board->det_pin = of_get_gpio(np, 2);
+
+       /* load the nfc driver if there is */
+       of_platform_populate(np, NULL, NULL, host->dev);
+
+       /*
+        * Initialize ECC mode to NAND_ECC_SOFT so that we have a correct value
+        * even if the nand-ecc-mode property is not defined.
+        */
+       host->nand_chip.ecc.mode = NAND_ECC_SOFT;
+       host->nand_chip.ecc.algo = NAND_ECC_HAMMING;
+
+       return 0;
+}
+
 static int atmel_hw_nand_init_params(struct platform_device *pdev,
                                         struct atmel_nand_host *host)
 {
@@ -1618,6 +1632,7 @@ static int atmel_hw_nand_init_params(struct platform_device *pdev,
                dev_err(host->dev,
                        "Can't get I/O resource regs, use software ECC\n");
                nand_chip->ecc.mode = NAND_ECC_SOFT;
+               nand_chip->ecc.algo = NAND_ECC_HAMMING;
                return 0;
        }
 
@@ -1631,25 +1646,26 @@ static int atmel_hw_nand_init_params(struct platform_device *pdev,
        /* set ECC page size and oob layout */
        switch (mtd->writesize) {
        case 512:
-               nand_chip->ecc.layout = &atmel_oobinfo_small;
+               mtd_set_ooblayout(mtd, &atmel_ooblayout_sp_ops);
                ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528);
                break;
        case 1024:
-               nand_chip->ecc.layout = &atmel_oobinfo_large;
+               mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
                ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056);
                break;
        case 2048:
-               nand_chip->ecc.layout = &atmel_oobinfo_large;
+               mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
                ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112);
                break;
        case 4096:
-               nand_chip->ecc.layout = &atmel_oobinfo_large;
+               mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
                ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224);
                break;
        default:
                /* page size not handled by HW ECC */
                /* switching back to soft ECC */
                nand_chip->ecc.mode = NAND_ECC_SOFT;
+               nand_chip->ecc.algo = NAND_ECC_HAMMING;
                return 0;
        }
 
@@ -2147,6 +2163,19 @@ static int atmel_nand_probe(struct platform_device *pdev)
        } else {
                memcpy(&host->board, dev_get_platdata(&pdev->dev),
                       sizeof(struct atmel_nand_data));
+               nand_chip->ecc.mode = host->board.ecc_mode;
+
+               /*
+                * When using software ECC every supported avr32 board means
+                * Hamming algorithm. If that ever changes we'll need to add
+                * ecc_algo field to the struct atmel_nand_data.
+                */
+               if (nand_chip->ecc.mode == NAND_ECC_SOFT)
+                       nand_chip->ecc.algo = NAND_ECC_HAMMING;
+
+               /* 16-bit bus width */
+               if (host->board.bus_width_16)
+                       nand_chip->options |= NAND_BUSWIDTH_16;
        }
 
         /* link the private data structures */
@@ -2188,11 +2217,8 @@ static int atmel_nand_probe(struct platform_device *pdev)
                nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;
        }
 
-       nand_chip->ecc.mode = host->board.ecc_mode;
        nand_chip->chip_delay = 40;             /* 40us command delay time */
 
-       if (host->board.bus_width_16)   /* 16-bit bus width */
-               nand_chip->options |= NAND_BUSWIDTH_16;
 
        nand_chip->read_buf = atmel_read_buf;
        nand_chip->write_buf = atmel_write_buf;
@@ -2225,11 +2251,6 @@ static int atmel_nand_probe(struct platform_device *pdev)
                }
        }
 
-       if (host->board.on_flash_bbt || on_flash_bbt) {
-               dev_info(&pdev->dev, "Use On Flash BBT\n");
-               nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
-       }
-
        if (!host->board.has_dma)
                use_dma = 0;
 
@@ -2256,6 +2277,18 @@ static int atmel_nand_probe(struct platform_device *pdev)
                goto err_scan_ident;
        }
 
+       if (host->board.on_flash_bbt || on_flash_bbt)
+               nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+       if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
+               dev_info(&pdev->dev, "Use On Flash BBT\n");
+
+       if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+               res = atmel_of_init_ecc(host, pdev->dev.of_node);
+               if (res)
+                       goto err_hw_ecc;
+       }
+
        if (nand_chip->ecc.mode == NAND_ECC_HW) {
                if (host->has_pmecc)
                        res = atmel_pmecc_nand_init_params(pdev, host);
index 341ea49..9bf6d99 100644 (file)
@@ -459,6 +459,7 @@ static int au1550nd_probe(struct platform_device *pdev)
        /* 30 us command delay time */
        this->chip_delay = 30;
        this->ecc.mode = NAND_ECC_SOFT;
+       this->ecc.algo = NAND_ECC_HAMMING;
 
        if (pd->devwidth)
                this->options |= NAND_BUSWIDTH_16;
index 7f6b30e..37da423 100644 (file)
@@ -109,28 +109,33 @@ static const unsigned short bfin_nfc_pin_req[] =
         0};
 
 #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
-static struct nand_ecclayout bootrom_ecclayout = {
-       .eccbytes = 24,
-       .eccpos = {
-               0x8 * 0, 0x8 * 0 + 1, 0x8 * 0 + 2,
-               0x8 * 1, 0x8 * 1 + 1, 0x8 * 1 + 2,
-               0x8 * 2, 0x8 * 2 + 1, 0x8 * 2 + 2,
-               0x8 * 3, 0x8 * 3 + 1, 0x8 * 3 + 2,
-               0x8 * 4, 0x8 * 4 + 1, 0x8 * 4 + 2,
-               0x8 * 5, 0x8 * 5 + 1, 0x8 * 5 + 2,
-               0x8 * 6, 0x8 * 6 + 1, 0x8 * 6 + 2,
-               0x8 * 7, 0x8 * 7 + 1, 0x8 * 7 + 2
-       },
-       .oobfree = {
-               { 0x8 * 0 + 3, 5 },
-               { 0x8 * 1 + 3, 5 },
-               { 0x8 * 2 + 3, 5 },
-               { 0x8 * 3 + 3, 5 },
-               { 0x8 * 4 + 3, 5 },
-               { 0x8 * 5 + 3, 5 },
-               { 0x8 * 6 + 3, 5 },
-               { 0x8 * 7 + 3, 5 },
-       }
+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
 
@@ -800,7 +805,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
        /* setup hardware ECC data struct */
        if (hardware_ecc) {
 #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
-               chip->ecc.layout = &bootrom_ecclayout;
+               mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops);
 #endif
                chip->read_buf      = bf5xx_nand_dma_read_buf;
                chip->write_buf     = bf5xx_nand_dma_write_buf;
@@ -812,6 +817,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
                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 */
index e052839..b76ad7c 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/of.h>
-#include <linux/of_mtd.h>
 #include <linux/of_platform.h>
 #include <linux/slab.h>
 #include <linux/list.h>
@@ -601,7 +600,7 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
 
 static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
 {
-       if (ctrl->nand_version < 0x0700)
+       if (ctrl->nand_version < 0x0602)
                return 24;
        return 0;
 }
@@ -781,127 +780,183 @@ static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg)
 }
 
 /*
- * Returns a nand_ecclayout strucutre for the given layout/configuration.
- * Returns NULL on failure.
+ * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given
+ * the layout/configuration.
+ * Returns -ERRCODE on failure.
  */
-static struct nand_ecclayout *brcmnand_create_layout(int ecc_level,
-                                                    struct brcmnand_host *host)
+static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                         struct mtd_oob_region *oobregion)
 {
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct brcmnand_host *host = nand_get_controller_data(chip);
        struct brcmnand_cfg *cfg = &host->hwcfg;
-       int i, j;
-       struct nand_ecclayout *layout;
-       int req;
-       int sectors;
-       int sas;
-       int idx1, idx2;
-
-       layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL);
-       if (!layout)
-               return NULL;
-
-       sectors = cfg->page_size / (512 << cfg->sector_size_1k);
-       sas = cfg->spare_area_size << cfg->sector_size_1k;
-
-       /* Hamming */
-       if (is_hamming_ecc(cfg)) {
-               for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
-                       /* First sector of each page may have BBI */
-                       if (i == 0) {
-                               layout->oobfree[idx2].offset = i * sas + 1;
-                               /* Small-page NAND use byte 6 for BBI */
-                               if (cfg->page_size == 512)
-                                       layout->oobfree[idx2].offset--;
-                               layout->oobfree[idx2].length = 5;
-                       } else {
-                               layout->oobfree[idx2].offset = i * sas;
-                               layout->oobfree[idx2].length = 6;
-                       }
-                       idx2++;
-                       layout->eccpos[idx1++] = i * sas + 6;
-                       layout->eccpos[idx1++] = i * sas + 7;
-                       layout->eccpos[idx1++] = i * sas + 8;
-                       layout->oobfree[idx2].offset = i * sas + 9;
-                       layout->oobfree[idx2].length = 7;
-                       idx2++;
-                       /* Leave zero-terminated entry for OOBFREE */
-                       if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
-                                   idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
-                               break;
-               }
+       int sas = cfg->spare_area_size << cfg->sector_size_1k;
+       int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
 
-               return layout;
-       }
+       if (section >= sectors)
+               return -ERANGE;
 
-       /*
-        * CONTROLLER_VERSION:
-        *   < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
-        *  >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
-        * But we will just be conservative.
-        */
-       req = DIV_ROUND_UP(ecc_level * 14, 8);
-       if (req >= sas) {
-               dev_err(&host->pdev->dev,
-                       "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
-                       req, sas);
-               return NULL;
-       }
+       oobregion->offset = (section * sas) + 6;
+       oobregion->length = 3;
+
+       return 0;
+}
+
+static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
+                                          struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct brcmnand_host *host = nand_get_controller_data(chip);
+       struct brcmnand_cfg *cfg = &host->hwcfg;
+       int sas = cfg->spare_area_size << cfg->sector_size_1k;
+       int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
 
-       layout->eccbytes = req * sectors;
-       for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
-               for (j = sas - req; j < sas && idx1 <
-                               MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++)
-                       layout->eccpos[idx1] = i * sas + j;
+       if (section >= sectors * 2)
+               return -ERANGE;
+
+       oobregion->offset = (section / 2) * sas;
+
+       if (section & 1) {
+               oobregion->offset += 9;
+               oobregion->length = 7;
+       } else {
+               oobregion->length = 6;
 
                /* First sector of each page may have BBI */
-               if (i == 0) {
-                       if (cfg->page_size == 512 && (sas - req >= 6)) {
-                               /* Small-page NAND use byte 6 for BBI */
-                               layout->oobfree[idx2].offset = 0;
-                               layout->oobfree[idx2].length = 5;
-                               idx2++;
-                               if (sas - req > 6) {
-                                       layout->oobfree[idx2].offset = 6;
-                                       layout->oobfree[idx2].length =
-                                               sas - req - 6;
-                                       idx2++;
-                               }
-                       } else if (sas > req + 1) {
-                               layout->oobfree[idx2].offset = i * sas + 1;
-                               layout->oobfree[idx2].length = sas - req - 1;
-                               idx2++;
-                       }
-               } else if (sas > req) {
-                       layout->oobfree[idx2].offset = i * sas;
-                       layout->oobfree[idx2].length = sas - req;
-                       idx2++;
+               if (!section) {
+                       /*
+                        * Small-page NAND use byte 6 for BBI while large-page
+                        * NAND use byte 0.
+                        */
+                       if (cfg->page_size > 512)
+                               oobregion->offset++;
+                       oobregion->length--;
                }
-               /* Leave zero-terminated entry for OOBFREE */
-               if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
-                               idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
-                       break;
        }
 
-       return layout;
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
+       .ecc = brcmnand_hamming_ooblayout_ecc,
+       .free = brcmnand_hamming_ooblayout_free,
+};
+
+static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                     struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct brcmnand_host *host = nand_get_controller_data(chip);
+       struct brcmnand_cfg *cfg = &host->hwcfg;
+       int sas = cfg->spare_area_size << cfg->sector_size_1k;
+       int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
+
+       if (section >= sectors)
+               return -ERANGE;
+
+       oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes;
+       oobregion->length = chip->ecc.bytes;
+
+       return 0;
+}
+
+static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
+                                         struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct brcmnand_host *host = nand_get_controller_data(chip);
+       struct brcmnand_cfg *cfg = &host->hwcfg;
+       int sas = cfg->spare_area_size << cfg->sector_size_1k;
+       int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
+
+       if (section >= sectors)
+               return -ERANGE;
+
+       if (sas <= chip->ecc.bytes)
+               return 0;
+
+       oobregion->offset = section * sas;
+       oobregion->length = sas - chip->ecc.bytes;
+
+       if (!section) {
+               oobregion->offset++;
+               oobregion->length--;
+       }
+
+       return 0;
 }
 
-static struct nand_ecclayout *brcmstb_choose_ecc_layout(
-               struct brcmnand_host *host)
+static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
+                                         struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct brcmnand_host *host = nand_get_controller_data(chip);
+       struct brcmnand_cfg *cfg = &host->hwcfg;
+       int sas = cfg->spare_area_size << cfg->sector_size_1k;
+
+       if (section > 1 || sas - chip->ecc.bytes < 6 ||
+           (section && sas - chip->ecc.bytes == 6))
+               return -ERANGE;
+
+       if (!section) {
+               oobregion->offset = 0;
+               oobregion->length = 5;
+       } else {
+               oobregion->offset = 6;
+               oobregion->length = sas - chip->ecc.bytes - 6;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = {
+       .ecc = brcmnand_bch_ooblayout_ecc,
+       .free = brcmnand_bch_ooblayout_free_lp,
+};
+
+static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = {
+       .ecc = brcmnand_bch_ooblayout_ecc,
+       .free = brcmnand_bch_ooblayout_free_sp,
+};
+
+static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
 {
-       struct nand_ecclayout *layout;
        struct brcmnand_cfg *p = &host->hwcfg;
+       struct mtd_info *mtd = nand_to_mtd(&host->chip);
+       struct nand_ecc_ctrl *ecc = &host->chip.ecc;
        unsigned int ecc_level = p->ecc_level;
+       int sas = p->spare_area_size << p->sector_size_1k;
+       int sectors = p->page_size / (512 << p->sector_size_1k);
 
        if (p->sector_size_1k)
                ecc_level <<= 1;
 
-       layout = brcmnand_create_layout(ecc_level, host);
-       if (!layout) {
+       if (is_hamming_ecc(p)) {
+               ecc->bytes = 3 * sectors;
+               mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
+               return 0;
+       }
+
+       /*
+        * CONTROLLER_VERSION:
+        *   < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
+        *  >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
+        * But we will just be conservative.
+        */
+       ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8);
+       if (p->page_size == 512)
+               mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops);
+       else
+               mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops);
+
+       if (ecc->bytes >= sas) {
                dev_err(&host->pdev->dev,
-                               "no proper ecc_layout for this NAND cfg\n");
-               return NULL;
+                       "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
+                       ecc->bytes, sas);
+               return -EINVAL;
        }
 
-       return layout;
+       return 0;
 }
 
 static void brcmnand_wp(struct mtd_info *mtd, int wp)
@@ -1870,9 +1925,31 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
        cfg->col_adr_bytes = 2;
        cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
 
+       if (chip->ecc.mode != NAND_ECC_HW) {
+               dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n",
+                       chip->ecc.mode);
+               return -EINVAL;
+       }
+
+       if (chip->ecc.algo == NAND_ECC_UNKNOWN) {
+               if (chip->ecc.strength == 1 && chip->ecc.size == 512)
+                       /* Default to Hamming for 1-bit ECC, if unspecified */
+                       chip->ecc.algo = NAND_ECC_HAMMING;
+               else
+                       /* Otherwise, BCH */
+                       chip->ecc.algo = NAND_ECC_BCH;
+       }
+
+       if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 ||
+                                                  chip->ecc.size != 512)) {
+               dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n",
+                       chip->ecc.strength, chip->ecc.size);
+               return -EINVAL;
+       }
+
        switch (chip->ecc.size) {
        case 512:
-               if (chip->ecc.strength == 1) /* Hamming */
+               if (chip->ecc.algo == NAND_ECC_HAMMING)
                        cfg->ecc_level = 15;
                else
                        cfg->ecc_level = chip->ecc.strength;
@@ -2001,8 +2078,8 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
         */
        chip->options |= NAND_USE_BOUNCE_BUFFER;
 
-       if (of_get_nand_on_flash_bbt(dn))
-               chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+       if (chip->bbt_options & NAND_BBT_USE_FLASH)
+               chip->bbt_options |= NAND_BBT_NO_OOB;
 
        if (brcmnand_setup_dev(host))
                return -ENXIO;
@@ -2011,9 +2088,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
        /* only use our internal HW threshold */
        mtd->bitflip_threshold = 1;
 
-       chip->ecc.layout = brcmstb_choose_ecc_layout(host);
-       if (!chip->ecc.layout)
-               return -ENXIO;
+       ret = brcmstb_choose_ecc_layout(host);
+       if (ret)
+               return ret;
 
        if (nand_scan_tail(mtd))
                return -ENXIO;
@@ -2115,6 +2192,7 @@ static const struct of_device_id brcmnand_of_match[] = {
        { .compatible = "brcm,brcmnand-v5.0" },
        { .compatible = "brcm,brcmnand-v6.0" },
        { .compatible = "brcm,brcmnand-v6.1" },
+       { .compatible = "brcm,brcmnand-v6.2" },
        { .compatible = "brcm,brcmnand-v7.0" },
        { .compatible = "brcm,brcmnand-v7.1" },
        {},
index e553aff..0b0c937 100644 (file)
@@ -459,10 +459,37 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
        return max_bitflips;
 }
 
-static struct nand_ecclayout cafe_oobinfo_2048 = {
-       .eccbytes = 14,
-       .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
-       .oobfree = {{14, 50}}
+static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section,
+                             struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 0;
+       oobregion->length = chip->ecc.total;
+
+       return 0;
+}
+
+static int cafe_ooblayout_free(struct mtd_info *mtd, int section,
+                              struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = chip->ecc.total;
+       oobregion->length = mtd->oobsize - chip->ecc.total;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops cafe_ooblayout_ops = {
+       .ecc = cafe_ooblayout_ecc,
+       .free = cafe_ooblayout_free,
 };
 
 /* Ick. The BBT code really ought to be able to work this bit out
@@ -494,12 +521,6 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = {
        .pattern = cafe_mirror_pattern_2048
 };
 
-static struct nand_ecclayout cafe_oobinfo_512 = {
-       .eccbytes = 14,
-       .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
-       .oobfree = {{14, 2}}
-};
-
 static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
        .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
                | NAND_BBT_2BIT | NAND_BBT_VERSION,
@@ -743,12 +764,11 @@ static int cafe_nand_probe(struct pci_dev *pdev,
                cafe->ctl2 |= 1<<29; /* 2KiB page size */
 
        /* Set up ECC according to the type of chip we found */
+       mtd_set_ooblayout(mtd, &cafe_ooblayout_ops);
        if (mtd->writesize == 2048) {
-               cafe->nand.ecc.layout = &cafe_oobinfo_2048;
                cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
                cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
        } else if (mtd->writesize == 512) {
-               cafe->nand.ecc.layout = &cafe_oobinfo_512;
                cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
                cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
        } else {
index 6f97ebb..4913378 100644 (file)
@@ -187,6 +187,7 @@ static int __init cmx270_init(void)
        /* 15 us command delay time */
        this->chip_delay = 20;
        this->ecc.mode = NAND_ECC_SOFT;
+       this->ecc.algo = NAND_ECC_HAMMING;
 
        /* read/write functions */
        this->read_byte = cmx270_read_byte;
index 8cb821b..cc07ba0 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/slab.h>
 #include <linux/of_device.h>
 #include <linux/of.h>
-#include <linux/of_mtd.h>
 
 #include <linux/platform_data/mtd-davinci.h>
 #include <linux/platform_data/mtd-davinci-aemif.h>
@@ -54,7 +53,6 @@
  */
 struct davinci_nand_info {
        struct nand_chip        chip;
-       struct nand_ecclayout   ecclayout;
 
        struct device           *dev;
        struct clk              *clk;
@@ -480,63 +478,46 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd)
  * ten ECC bytes plus the manufacturer's bad block marker byte, and
  * and not overlapping the default BBT markers.
  */
-static struct nand_ecclayout hwecc4_small = {
-       .eccbytes = 10,
-       .eccpos = { 0, 1, 2, 3, 4,
-               /* offset 5 holds the badblock marker */
-               6, 7,
-               13, 14, 15, },
-       .oobfree = {
-               {.offset = 8, .length = 5, },
-               {.offset = 16, },
-       },
-};
+static int hwecc4_ooblayout_small_ecc(struct mtd_info *mtd, int section,
+                                     struct mtd_oob_region *oobregion)
+{
+       if (section > 2)
+               return -ERANGE;
+
+       if (!section) {
+               oobregion->offset = 0;
+               oobregion->length = 5;
+       } else if (section == 1) {
+               oobregion->offset = 6;
+               oobregion->length = 2;
+       } else {
+               oobregion->offset = 13;
+               oobregion->length = 3;
+       }
 
-/* An ECC layout for using 4-bit ECC with large-page (2048bytes) flash,
- * storing ten ECC bytes plus the manufacturer's bad block marker byte,
- * and not overlapping the default BBT markers.
- */
-static struct nand_ecclayout hwecc4_2048 = {
-       .eccbytes = 40,
-       .eccpos = {
-               /* at the end of spare sector */
-               24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
-               34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
-               44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
-               54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-               },
-       .oobfree = {
-               /* 2 bytes at offset 0 hold manufacturer badblock markers */
-               {.offset = 2, .length = 22, },
-               /* 5 bytes at offset 8 hold BBT markers */
-               /* 8 bytes at offset 16 hold JFFS2 clean markers */
-       },
-};
+       return 0;
+}
 
-/*
- * An ECC layout for using 4-bit ECC with large-page (4096bytes) flash,
- * storing ten ECC bytes plus the manufacturer's bad block marker byte,
- * and not overlapping the default BBT markers.
- */
-static struct nand_ecclayout hwecc4_4096 = {
-       .eccbytes = 80,
-       .eccpos = {
-               /* at the end of spare sector */
-               48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
-               58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
-               68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
-               78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
-               88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
-               98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
-               108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
-               118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
-       },
-       .oobfree = {
-               /* 2 bytes at offset 0 hold manufacturer badblock markers */
-               {.offset = 2, .length = 46, },
-               /* 5 bytes at offset 8 hold BBT markers */
-               /* 8 bytes at offset 16 hold JFFS2 clean markers */
-       },
+static int hwecc4_ooblayout_small_free(struct mtd_info *mtd, int section,
+                                      struct mtd_oob_region *oobregion)
+{
+       if (section > 1)
+               return -ERANGE;
+
+       if (!section) {
+               oobregion->offset = 8;
+               oobregion->length = 5;
+       } else {
+               oobregion->offset = 16;
+               oobregion->length = mtd->oobsize - 16;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops hwecc4_small_ooblayout_ops = {
+       .ecc = hwecc4_ooblayout_small_ecc,
+       .free = hwecc4_ooblayout_small_free,
 };
 
 #if defined(CONFIG_OF)
@@ -577,8 +558,6 @@ static struct davinci_nand_pdata
                        "ti,davinci-mask-chipsel", &prop))
                        pdata->mask_chipsel = prop;
                if (!of_property_read_string(pdev->dev.of_node,
-                       "nand-ecc-mode", &mode) ||
-                   !of_property_read_string(pdev->dev.of_node,
                        "ti,davinci-ecc-mode", &mode)) {
                        if (!strncmp("none", mode, 4))
                                pdata->ecc_mode = NAND_ECC_NONE;
@@ -591,14 +570,11 @@ static struct davinci_nand_pdata
                        "ti,davinci-ecc-bits", &prop))
                        pdata->ecc_bits = prop;
 
-               prop = of_get_nand_bus_width(pdev->dev.of_node);
-               if (0 < prop || !of_property_read_u32(pdev->dev.of_node,
-                       "ti,davinci-nand-buswidth", &prop))
-                       if (prop == 16)
-                               pdata->options |= NAND_BUSWIDTH_16;
+               if (!of_property_read_u32(pdev->dev.of_node,
+                       "ti,davinci-nand-buswidth", &prop) && prop == 16)
+                       pdata->options |= NAND_BUSWIDTH_16;
+
                if (of_property_read_bool(pdev->dev.of_node,
-                       "nand-on-flash-bbt") ||
-                   of_property_read_bool(pdev->dev.of_node,
                        "ti,davinci-nand-use-bbt"))
                        pdata->bbt_options = NAND_BBT_USE_FLASH;
 
@@ -628,7 +604,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
        void __iomem                    *base;
        int                             ret;
        uint32_t                        val;
-       nand_ecc_modes_t                ecc_mode;
        struct mtd_info                 *mtd;
 
        pdata = nand_davinci_get_pdata(pdev);
@@ -712,13 +687,53 @@ static int nand_davinci_probe(struct platform_device *pdev)
        info->chip.write_buf    = nand_davinci_write_buf;
 
        /* Use board-specific ECC config */
-       ecc_mode                = pdata->ecc_mode;
+       info->chip.ecc.mode     = pdata->ecc_mode;
 
        ret = -EINVAL;
-       switch (ecc_mode) {
+
+       info->clk = devm_clk_get(&pdev->dev, "aemif");
+       if (IS_ERR(info->clk)) {
+               ret = PTR_ERR(info->clk);
+               dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(info->clk);
+       if (ret < 0) {
+               dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
+                       ret);
+               goto err_clk_enable;
+       }
+
+       spin_lock_irq(&davinci_nand_lock);
+
+       /* put CSxNAND into NAND mode */
+       val = davinci_nand_readl(info, NANDFCR_OFFSET);
+       val |= BIT(info->core_chipsel);
+       davinci_nand_writel(info, NANDFCR_OFFSET, val);
+
+       spin_unlock_irq(&davinci_nand_lock);
+
+       /* Scan to find existence of the device(s) */
+       ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL);
+       if (ret < 0) {
+               dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
+               goto err;
+       }
+
+       switch (info->chip.ecc.mode) {
        case NAND_ECC_NONE:
+               pdata->ecc_bits = 0;
+               break;
        case NAND_ECC_SOFT:
                pdata->ecc_bits = 0;
+               /*
+                * This driver expects Hamming based ECC when ecc_mode is set
+                * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
+                * avoid adding an extra ->ecc_algo field to
+                * davinci_nand_pdata.
+                */
+               info->chip.ecc.algo = NAND_ECC_HAMMING;
                break;
        case NAND_ECC_HW:
                if (pdata->ecc_bits == 4) {
@@ -754,37 +769,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
        default:
                return -EINVAL;
        }
-       info->chip.ecc.mode = ecc_mode;
-
-       info->clk = devm_clk_get(&pdev->dev, "aemif");
-       if (IS_ERR(info->clk)) {
-               ret = PTR_ERR(info->clk);
-               dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
-               return ret;
-       }
-
-       ret = clk_prepare_enable(info->clk);
-       if (ret < 0) {
-               dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
-                       ret);
-               goto err_clk_enable;
-       }
-
-       spin_lock_irq(&davinci_nand_lock);
-
-       /* put CSxNAND into NAND mode */
-       val = davinci_nand_readl(info, NANDFCR_OFFSET);
-       val |= BIT(info->core_chipsel);
-       davinci_nand_writel(info, NANDFCR_OFFSET, val);
-
-       spin_unlock_irq(&davinci_nand_lock);
-
-       /* Scan to find existence of the device(s) */
-       ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL);
-       if (ret < 0) {
-               dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
-               goto err;
-       }
 
        /* Update ECC layout if needed ... for 1-bit HW ECC, the default
         * is OK, but it allocates 6 bytes when only 3 are needed (for
@@ -805,26 +789,14 @@ static int nand_davinci_probe(struct platform_device *pdev)
                 * table marker fits in the free bytes.
                 */
                if (chunks == 1) {
-                       info->ecclayout = hwecc4_small;
-                       info->ecclayout.oobfree[1].length = mtd->oobsize - 16;
-                       goto syndrome_done;
-               }
-               if (chunks == 4) {
-                       info->ecclayout = hwecc4_2048;
-                       info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
-                       goto syndrome_done;
-               }
-               if (chunks == 8) {
-                       info->ecclayout = hwecc4_4096;
+                       mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops);
+               } else if (chunks == 4 || chunks == 8) {
+                       mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
                        info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
-                       goto syndrome_done;
+               } else {
+                       ret = -EIO;
+                       goto err;
                }
-
-               ret = -EIO;
-               goto err;
-
-syndrome_done:
-               info->chip.ecc.layout = &info->ecclayout;
        }
 
        ret = nand_scan_tail(mtd);
@@ -850,7 +822,7 @@ err:
 
 err_clk_enable:
        spin_lock_irq(&davinci_nand_lock);
-       if (ecc_mode == NAND_ECC_HW_SYNDROME)
+       if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
                ecc4_busy = false;
        spin_unlock_irq(&davinci_nand_lock);
        return ret;
index 30bf5f6..0476ae8 100644 (file)
@@ -1374,13 +1374,41 @@ static void denali_hw_init(struct denali_nand_info *denali)
  * correction
  */
 #define ECC_8BITS      14
-static struct nand_ecclayout nand_8bit_oob = {
-       .eccbytes = 14,
-};
-
 #define ECC_15BITS     26
-static struct nand_ecclayout nand_15bit_oob = {
-       .eccbytes = 26,
+
+static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
+                               struct mtd_oob_region *oobregion)
+{
+       struct denali_nand_info *denali = mtd_to_denali(mtd);
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = denali->bbtskipbytes;
+       oobregion->length = chip->ecc.total;
+
+       return 0;
+}
+
+static int denali_ooblayout_free(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct denali_nand_info *denali = mtd_to_denali(mtd);
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = chip->ecc.total + denali->bbtskipbytes;
+       oobregion->length = mtd->oobsize - oobregion->offset;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
+       .ecc = denali_ooblayout_ecc,
+       .free = denali_ooblayout_free,
 };
 
 static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
@@ -1561,7 +1589,6 @@ int denali_init(struct denali_nand_info *denali)
                        ECC_SECTOR_SIZE)))) {
                /* if MLC OOB size is large enough, use 15bit ECC*/
                denali->nand.ecc.strength = 15;
-               denali->nand.ecc.layout = &nand_15bit_oob;
                denali->nand.ecc.bytes = ECC_15BITS;
                iowrite32(15, denali->flash_reg + ECC_CORRECTION);
        } else if (mtd->oobsize < (denali->bbtskipbytes +
@@ -1571,20 +1598,13 @@ int denali_init(struct denali_nand_info *denali)
                goto failed_req_irq;
        } else {
                denali->nand.ecc.strength = 8;
-               denali->nand.ecc.layout = &nand_8bit_oob;
                denali->nand.ecc.bytes = ECC_8BITS;
                iowrite32(8, denali->flash_reg + ECC_CORRECTION);
        }
 
+       mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
        denali->nand.ecc.bytes *= denali->devnum;
        denali->nand.ecc.strength *= denali->devnum;
-       denali->nand.ecc.layout->eccbytes *=
-               mtd->writesize / ECC_SECTOR_SIZE;
-       denali->nand.ecc.layout->oobfree[0].offset =
-               denali->bbtskipbytes + denali->nand.ecc.layout->eccbytes;
-       denali->nand.ecc.layout->oobfree[0].length =
-               mtd->oobsize - denali->nand.ecc.layout->eccbytes -
-               denali->bbtskipbytes;
 
        /*
         * Let driver know the total blocks number and how many blocks
index 547c100..a023ab9 100644 (file)
@@ -950,20 +950,50 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
 
 //u_char mydatabuf[528];
 
-/* The strange out-of-order .oobfree list below is a (possibly unneeded)
- * attempt to retain compatibility.  It used to read:
- *     .oobfree = { {8, 8} }
- * Since that leaves two bytes unusable, it was changed.  But the following
- * scheme might affect existing jffs2 installs by moving the cleanmarker:
- *     .oobfree = { {6, 10} }
- * jffs2 seems to handle the above gracefully, but the current scheme seems
- * safer.  The only problem with it is that any code that parses oobfree must
- * be able to handle out-of-order segments.
- */
-static struct nand_ecclayout doc200x_oobinfo = {
-       .eccbytes = 6,
-       .eccpos = {0, 1, 2, 3, 4, 5},
-       .oobfree = {{8, 8}, {6, 2}}
+static int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 0;
+       oobregion->length = 6;
+
+       return 0;
+}
+
+static int doc200x_ooblayout_free(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       if (section > 1)
+               return -ERANGE;
+
+       /*
+        * The strange out-of-order free bytes definition is a (possibly
+        * unneeded) attempt to retain compatibility.  It used to read:
+        *      .oobfree = { {8, 8} }
+        * Since that leaves two bytes unusable, it was changed.  But the
+        * following scheme might affect existing jffs2 installs by moving the
+        * cleanmarker:
+        *      .oobfree = { {6, 10} }
+        * jffs2 seems to handle the above gracefully, but the current scheme
+        * seems safer. The only problem with it is that any code retrieving
+        * free bytes position must be able to handle out-of-order segments.
+        */
+       if (!section) {
+               oobregion->offset = 8;
+               oobregion->length = 8;
+       } else {
+               oobregion->offset = 6;
+               oobregion->length = 2;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops doc200x_ooblayout_ops = {
+       .ecc = doc200x_ooblayout_ecc,
+       .free = doc200x_ooblayout_free,
 };
 
 /* Find the (I)NFTL Media Header, and optionally also the mirror media header.
@@ -1537,6 +1567,7 @@ static int __init doc_probe(unsigned long physadr)
        nand->bbt_md            = nand->bbt_td + 1;
 
        mtd->owner              = THIS_MODULE;
+       mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops);
 
        nand_set_controller_data(nand, doc);
        nand->select_chip       = doc200x_select_chip;
@@ -1548,7 +1579,6 @@ static int __init doc_probe(unsigned long physadr)
        nand->ecc.calculate     = doc200x_calculate_ecc;
        nand->ecc.correct       = doc200x_correct_data;
 
-       nand->ecc.layout        = &doc200x_oobinfo;
        nand->ecc.mode          = NAND_ECC_HW_SYNDROME;
        nand->ecc.size          = 512;
        nand->ecc.bytes         = 6;
index d86a60e..4731699 100644 (file)
@@ -222,10 +222,33 @@ struct docg4_priv {
  * Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14.
  * Byte 15 (the last) is used by the driver as a "page written" flag.
  */
-static struct nand_ecclayout docg4_oobinfo = {
-       .eccbytes = 9,
-       .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
-       .oobfree = { {.offset = 2, .length = 5} }
+static int docg4_ooblayout_ecc(struct mtd_info *mtd, int section,
+                              struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 7;
+       oobregion->length = 9;
+
+       return 0;
+}
+
+static int docg4_ooblayout_free(struct mtd_info *mtd, int section,
+                               struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 2;
+       oobregion->length = 5;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops docg4_ooblayout_ops = {
+       .ecc = docg4_ooblayout_ecc,
+       .free = docg4_ooblayout_free,
 };
 
 /*
@@ -1209,6 +1232,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
        mtd->writesize = DOCG4_PAGE_SIZE;
        mtd->erasesize = DOCG4_BLOCK_SIZE;
        mtd->oobsize = DOCG4_OOB_SIZE;
+       mtd_set_ooblayout(mtd, &docg4_ooblayout_ops);
        nand->chipsize = DOCG4_CHIP_SIZE;
        nand->chip_shift = DOCG4_CHIP_SHIFT;
        nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT;
@@ -1217,7 +1241,6 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
        nand->pagemask = 0x3ffff;
        nand->badblockpos = NAND_LARGE_BADBLOCK_POS;
        nand->badblockbits = 8;
-       nand->ecc.layout = &docg4_oobinfo;
        nand->ecc.mode = NAND_ECC_HW_SYNDROME;
        nand->ecc.size = DOCG4_PAGE_SIZE;
        nand->ecc.prepad = 8;
index 059d5f7..60a88f2 100644 (file)
@@ -79,32 +79,53 @@ struct fsl_elbc_fcm_ctrl {
 
 /* These map to the positions used by the FCM hardware ECC generator */
 
-/* Small Page FLASH with FMR[ECCM] = 0 */
-static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = {
-       .eccbytes = 3,
-       .eccpos = {6, 7, 8},
-       .oobfree = { {0, 5}, {9, 7} },
-};
+static int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
 
-/* Small Page FLASH with FMR[ECCM] = 1 */
-static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = {
-       .eccbytes = 3,
-       .eccpos = {8, 9, 10},
-       .oobfree = { {0, 5}, {6, 2}, {11, 5} },
-};
+       if (section >= chip->ecc.steps)
+               return -ERANGE;
 
-/* Large Page FLASH with FMR[ECCM] = 0 */
-static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = {
-       .eccbytes = 12,
-       .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},
-       .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} },
-};
+       oobregion->offset = (16 * section) + 6;
+       if (priv->fmr & FMR_ECCM)
+               oobregion->offset += 2;
 
-/* Large Page FLASH with FMR[ECCM] = 1 */
-static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
-       .eccbytes = 12,
-       .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},
-       .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },
+       oobregion->length = chip->ecc.bytes;
+
+       return 0;
+}
+
+static int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+
+       if (section > chip->ecc.steps)
+               return -ERANGE;
+
+       if (!section) {
+               oobregion->offset = 0;
+               if (mtd->writesize > 512)
+                       oobregion->offset++;
+               oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5;
+       } else {
+               oobregion->offset = (16 * section) -
+                                   ((priv->fmr & FMR_ECCM) ? 5 : 7);
+               if (section < chip->ecc.steps)
+                       oobregion->length = 13;
+               else
+                       oobregion->length = mtd->oobsize - oobregion->offset;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = {
+       .ecc = fsl_elbc_ooblayout_ecc,
+       .free = fsl_elbc_ooblayout_free,
 };
 
 /*
@@ -657,8 +678,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
                chip->ecc.bytes);
        dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
                chip->ecc.total);
-       dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
-               chip->ecc.layout);
+       dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n",
+               mtd->ooblayout);
        dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
        dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
        dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
@@ -675,14 +696,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
        } else if (mtd->writesize == 2048) {
                priv->page_size = 1;
                setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
-               /* adjust ecc setup if needed */
-               if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
-                   BR_DECC_CHK_GEN) {
-                       chip->ecc.size = 512;
-                       chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
-                                          &fsl_elbc_oob_lp_eccm1 :
-                                          &fsl_elbc_oob_lp_eccm0;
-               }
        } else {
                dev_err(priv->dev,
                        "fsl_elbc_init: page size %d is not supported\n",
@@ -780,15 +793,14 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
        if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
            BR_DECC_CHK_GEN) {
                chip->ecc.mode = NAND_ECC_HW;
-               /* put in small page settings and adjust later if needed */
-               chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
-                               &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0;
+               mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
                chip->ecc.size = 512;
                chip->ecc.bytes = 3;
                chip->ecc.strength = 1;
        } else {
                /* otherwise fall back to default software ECC */
                chip->ecc.mode = NAND_ECC_SOFT;
+               chip->ecc.algo = NAND_ECC_HAMMING;
        }
 
        return 0;
index 43f5a3a..4e9e5fd 100644 (file)
@@ -67,136 +67,6 @@ struct fsl_ifc_nand_ctrl {
 
 static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl;
 
-/* 512-byte page with 4-bit ECC, 8-bit */
-static struct nand_ecclayout oob_512_8bit_ecc4 = {
-       .eccbytes = 8,
-       .eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
-       .oobfree = { {0, 5}, {6, 2} },
-};
-
-/* 512-byte page with 4-bit ECC, 16-bit */
-static struct nand_ecclayout oob_512_16bit_ecc4 = {
-       .eccbytes = 8,
-       .eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
-       .oobfree = { {2, 6}, },
-};
-
-/* 2048-byte page size with 4-bit ECC */
-static struct nand_ecclayout oob_2048_ecc4 = {
-       .eccbytes = 32,
-       .eccpos = {
-               8, 9, 10, 11, 12, 13, 14, 15,
-               16, 17, 18, 19, 20, 21, 22, 23,
-               24, 25, 26, 27, 28, 29, 30, 31,
-               32, 33, 34, 35, 36, 37, 38, 39,
-       },
-       .oobfree = { {2, 6}, {40, 24} },
-};
-
-/* 4096-byte page size with 4-bit ECC */
-static struct nand_ecclayout oob_4096_ecc4 = {
-       .eccbytes = 64,
-       .eccpos = {
-               8, 9, 10, 11, 12, 13, 14, 15,
-               16, 17, 18, 19, 20, 21, 22, 23,
-               24, 25, 26, 27, 28, 29, 30, 31,
-               32, 33, 34, 35, 36, 37, 38, 39,
-               40, 41, 42, 43, 44, 45, 46, 47,
-               48, 49, 50, 51, 52, 53, 54, 55,
-               56, 57, 58, 59, 60, 61, 62, 63,
-               64, 65, 66, 67, 68, 69, 70, 71,
-       },
-       .oobfree = { {2, 6}, {72, 56} },
-};
-
-/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */
-static struct nand_ecclayout oob_4096_ecc8 = {
-       .eccbytes = 128,
-       .eccpos = {
-               8, 9, 10, 11, 12, 13, 14, 15,
-               16, 17, 18, 19, 20, 21, 22, 23,
-               24, 25, 26, 27, 28, 29, 30, 31,
-               32, 33, 34, 35, 36, 37, 38, 39,
-               40, 41, 42, 43, 44, 45, 46, 47,
-               48, 49, 50, 51, 52, 53, 54, 55,
-               56, 57, 58, 59, 60, 61, 62, 63,
-               64, 65, 66, 67, 68, 69, 70, 71,
-               72, 73, 74, 75, 76, 77, 78, 79,
-               80, 81, 82, 83, 84, 85, 86, 87,
-               88, 89, 90, 91, 92, 93, 94, 95,
-               96, 97, 98, 99, 100, 101, 102, 103,
-               104, 105, 106, 107, 108, 109, 110, 111,
-               112, 113, 114, 115, 116, 117, 118, 119,
-               120, 121, 122, 123, 124, 125, 126, 127,
-               128, 129, 130, 131, 132, 133, 134, 135,
-       },
-       .oobfree = { {2, 6}, {136, 82} },
-};
-
-/* 8192-byte page size with 4-bit ECC */
-static struct nand_ecclayout oob_8192_ecc4 = {
-       .eccbytes = 128,
-       .eccpos = {
-               8, 9, 10, 11, 12, 13, 14, 15,
-               16, 17, 18, 19, 20, 21, 22, 23,
-               24, 25, 26, 27, 28, 29, 30, 31,
-               32, 33, 34, 35, 36, 37, 38, 39,
-               40, 41, 42, 43, 44, 45, 46, 47,
-               48, 49, 50, 51, 52, 53, 54, 55,
-               56, 57, 58, 59, 60, 61, 62, 63,
-               64, 65, 66, 67, 68, 69, 70, 71,
-               72, 73, 74, 75, 76, 77, 78, 79,
-               80, 81, 82, 83, 84, 85, 86, 87,
-               88, 89, 90, 91, 92, 93, 94, 95,
-               96, 97, 98, 99, 100, 101, 102, 103,
-               104, 105, 106, 107, 108, 109, 110, 111,
-               112, 113, 114, 115, 116, 117, 118, 119,
-               120, 121, 122, 123, 124, 125, 126, 127,
-               128, 129, 130, 131, 132, 133, 134, 135,
-       },
-       .oobfree = { {2, 6}, {136, 208} },
-};
-
-/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */
-static struct nand_ecclayout oob_8192_ecc8 = {
-       .eccbytes = 256,
-       .eccpos = {
-               8, 9, 10, 11, 12, 13, 14, 15,
-               16, 17, 18, 19, 20, 21, 22, 23,
-               24, 25, 26, 27, 28, 29, 30, 31,
-               32, 33, 34, 35, 36, 37, 38, 39,
-               40, 41, 42, 43, 44, 45, 46, 47,
-               48, 49, 50, 51, 52, 53, 54, 55,
-               56, 57, 58, 59, 60, 61, 62, 63,
-               64, 65, 66, 67, 68, 69, 70, 71,
-               72, 73, 74, 75, 76, 77, 78, 79,
-               80, 81, 82, 83, 84, 85, 86, 87,
-               88, 89, 90, 91, 92, 93, 94, 95,
-               96, 97, 98, 99, 100, 101, 102, 103,
-               104, 105, 106, 107, 108, 109, 110, 111,
-               112, 113, 114, 115, 116, 117, 118, 119,
-               120, 121, 122, 123, 124, 125, 126, 127,
-               128, 129, 130, 131, 132, 133, 134, 135,
-               136, 137, 138, 139, 140, 141, 142, 143,
-               144, 145, 146, 147, 148, 149, 150, 151,
-               152, 153, 154, 155, 156, 157, 158, 159,
-               160, 161, 162, 163, 164, 165, 166, 167,
-               168, 169, 170, 171, 172, 173, 174, 175,
-               176, 177, 178, 179, 180, 181, 182, 183,
-               184, 185, 186, 187, 188, 189, 190, 191,
-               192, 193, 194, 195, 196, 197, 198, 199,
-               200, 201, 202, 203, 204, 205, 206, 207,
-               208, 209, 210, 211, 212, 213, 214, 215,
-               216, 217, 218, 219, 220, 221, 222, 223,
-               224, 225, 226, 227, 228, 229, 230, 231,
-               232, 233, 234, 235, 236, 237, 238, 239,
-               240, 241, 242, 243, 244, 245, 246, 247,
-               248, 249, 250, 251, 252, 253, 254, 255,
-               256, 257, 258, 259, 260, 261, 262, 263,
-       },
-       .oobfree = { {2, 6}, {264, 80} },
-};
-
 /*
  * Generic flash bbt descriptors
  */
@@ -223,6 +93,57 @@ static struct nand_bbt_descr bbt_mirror_descr = {
        .pattern = mirror_pattern,
 };
 
+static int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 8;
+       oobregion->length = chip->ecc.total;
+
+       return 0;
+}
+
+static int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section > 1)
+               return -ERANGE;
+
+       if (mtd->writesize == 512 &&
+           !(chip->options & NAND_BUSWIDTH_16)) {
+               if (!section) {
+                       oobregion->offset = 0;
+                       oobregion->length = 5;
+               } else {
+                       oobregion->offset = 6;
+                       oobregion->length = 2;
+               }
+
+               return 0;
+       }
+
+       if (!section) {
+               oobregion->offset = 2;
+               oobregion->length = 6;
+       } else {
+               oobregion->offset = chip->ecc.total + 8;
+               oobregion->length = mtd->oobsize - oobregion->offset;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = {
+       .ecc = fsl_ifc_ooblayout_ecc,
+       .free = fsl_ifc_ooblayout_free,
+};
+
 /*
  * Set up the IFC hardware block and page address fields, and the ifc nand
  * structure addr field to point to the correct IFC buffer in memory
@@ -232,7 +153,7 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
        struct nand_chip *chip = mtd_to_nand(mtd);
        struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_ifc_ctrl *ctrl = priv->ctrl;
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
        int buf_num;
 
        ifc_nand_ctrl->page = page_addr;
@@ -257,18 +178,22 @@ static int is_blank(struct mtd_info *mtd, unsigned int bufnum)
        u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2);
        u32 __iomem *mainarea = (u32 __iomem *)addr;
        u8 __iomem *oob = addr + mtd->writesize;
-       int i;
+       struct mtd_oob_region oobregion = { };
+       int i, section = 0;
 
        for (i = 0; i < mtd->writesize / 4; i++) {
                if (__raw_readl(&mainarea[i]) != 0xffffffff)
                        return 0;
        }
 
-       for (i = 0; i < chip->ecc.layout->eccbytes; i++) {
-               int pos = chip->ecc.layout->eccpos[i];
+       mtd_ooblayout_ecc(mtd, section++, &oobregion);
+       while (oobregion.length) {
+               for (i = 0; i < oobregion.length; i++) {
+                       if (__raw_readb(&oob[oobregion.offset + i]) != 0xff)
+                               return 0;
+               }
 
-               if (__raw_readb(&oob[pos]) != 0xff)
-                       return 0;
+               mtd_ooblayout_ecc(mtd, section++, &oobregion);
        }
 
        return 1;
@@ -295,7 +220,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
        struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_ifc_ctrl *ctrl = priv->ctrl;
        struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
        u32 eccstat[4];
        int i;
 
@@ -371,7 +296,7 @@ static void fsl_ifc_do_read(struct nand_chip *chip,
 {
        struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_ifc_ctrl *ctrl = priv->ctrl;
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
 
        /* Program FIR/IFC_NAND_FCR0 for Small/Large page */
        if (mtd->writesize > 512) {
@@ -411,7 +336,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
        struct nand_chip *chip = mtd_to_nand(mtd);
        struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_ifc_ctrl *ctrl = priv->ctrl;
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
 
        /* clear the read buffer */
        ifc_nand_ctrl->read_bytes = 0;
@@ -723,7 +648,7 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
 {
        struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_ifc_ctrl *ctrl = priv->ctrl;
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
        u32 nand_fsr;
 
        /* Use READ_STATUS command, but wait for the device to be ready */
@@ -808,8 +733,8 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
                                                        chip->ecc.bytes);
        dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__,
                                                        chip->ecc.total);
-       dev_dbg(priv->dev, "%s: nand->ecc.layout = %p\n", __func__,
-                                                       chip->ecc.layout);
+       dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__,
+                                                       mtd->ooblayout);
        dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags);
        dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size);
        dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__,
@@ -825,39 +750,42 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
 static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
 {
        struct fsl_ifc_ctrl *ctrl = priv->ctrl;
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs;
+       struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs;
        uint32_t csor = 0, csor_8k = 0, csor_ext = 0;
        uint32_t cs = priv->bank;
 
        /* Save CSOR and CSOR_ext */
-       csor = ifc_in32(&ifc->csor_cs[cs].csor);
-       csor_ext = ifc_in32(&ifc->csor_cs[cs].csor_ext);
+       csor = ifc_in32(&ifc_global->csor_cs[cs].csor);
+       csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext);
 
        /* chage PageSize 8K and SpareSize 1K*/
        csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000;
-       ifc_out32(csor_8k, &ifc->csor_cs[cs].csor);
-       ifc_out32(0x0000400, &ifc->csor_cs[cs].csor_ext);
+       ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor);
+       ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext);
 
        /* READID */
        ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
-                 (IFC_FIR_OP_UA  << IFC_NAND_FIR0_OP1_SHIFT) |
-                 (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT),
-                 &ifc->ifc_nand.nand_fir0);
+                   (IFC_FIR_OP_UA  << IFC_NAND_FIR0_OP1_SHIFT) |
+                   (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT),
+                   &ifc_runtime->ifc_nand.nand_fir0);
        ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT,
-                 &ifc->ifc_nand.nand_fcr0);
-       ifc_out32(0x0, &ifc->ifc_nand.row3);
+                   &ifc_runtime->ifc_nand.nand_fcr0);
+       ifc_out32(0x0, &ifc_runtime->ifc_nand.row3);
 
-       ifc_out32(0x0, &ifc->ifc_nand.nand_fbcr);
+       ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr);
 
        /* Program ROW0/COL0 */
-       ifc_out32(0x0, &ifc->ifc_nand.row0);
-       ifc_out32(0x0, &ifc->ifc_nand.col0);
+       ifc_out32(0x0, &ifc_runtime->ifc_nand.row0);
+       ifc_out32(0x0, &ifc_runtime->ifc_nand.col0);
 
        /* set the chip select for NAND Transaction */
-       ifc_out32(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel);
+       ifc_out32(cs << IFC_NAND_CSEL_SHIFT,
+               &ifc_runtime->ifc_nand.nand_csel);
 
        /* start read seq */
-       ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
+       ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT,
+               &ifc_runtime->ifc_nand.nandseq_strt);
 
        /* wait for command complete flag or timeout */
        wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
@@ -867,17 +795,17 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
                printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n");
 
        /* Restore CSOR and CSOR_ext */
-       ifc_out32(csor, &ifc->csor_cs[cs].csor);
-       ifc_out32(csor_ext, &ifc->csor_cs[cs].csor_ext);
+       ifc_out32(csor, &ifc_global->csor_cs[cs].csor);
+       ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext);
 }
 
 static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
 {
        struct fsl_ifc_ctrl *ctrl = priv->ctrl;
-       struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
+       struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs;
+       struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs;
        struct nand_chip *chip = &priv->chip;
        struct mtd_info *mtd = nand_to_mtd(&priv->chip);
-       struct nand_ecclayout *layout;
        u32 csor;
 
        /* Fill in fsl_ifc_mtd structure */
@@ -886,7 +814,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
 
        /* fill in nand_chip structure */
        /* set up function call table */
-       if ((ifc_in32(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16)
+       if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr))
+               & CSPR_PORT_SIZE_16)
                chip->read_byte = fsl_ifc_read_byte16;
        else
                chip->read_byte = fsl_ifc_read_byte;
@@ -900,13 +829,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
        chip->bbt_td = &bbt_main_descr;
        chip->bbt_md = &bbt_mirror_descr;
 
-       ifc_out32(0x0, &ifc->ifc_nand.ncfgr);
+       ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr);
 
        /* set up nand options */
        chip->bbt_options = NAND_BBT_USE_FLASH;
        chip->options = NAND_NO_SUBPAGE_WRITE;
 
-       if (ifc_in32(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) {
+       if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)
+               & CSPR_PORT_SIZE_16) {
                chip->read_byte = fsl_ifc_read_byte16;
                chip->options |= NAND_BUSWIDTH_16;
        } else {
@@ -919,20 +849,11 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
        chip->ecc.read_page = fsl_ifc_read_page;
        chip->ecc.write_page = fsl_ifc_write_page;
 
-       csor = ifc_in32(&ifc->csor_cs[priv->bank].csor);
-
-       /* Hardware generates ECC per 512 Bytes */
-       chip->ecc.size = 512;
-       chip->ecc.bytes = 8;
-       chip->ecc.strength = 4;
+       csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor);
 
        switch (csor & CSOR_NAND_PGS_MASK) {
        case CSOR_NAND_PGS_512:
-               if (chip->options & NAND_BUSWIDTH_16) {
-                       layout = &oob_512_16bit_ecc4;
-               } else {
-                       layout = &oob_512_8bit_ecc4;
-
+               if (!(chip->options & NAND_BUSWIDTH_16)) {
                        /* Avoid conflict with bad block marker */
                        bbt_main_descr.offs = 0;
                        bbt_mirror_descr.offs = 0;
@@ -942,35 +863,16 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
                break;
 
        case CSOR_NAND_PGS_2K:
-               layout = &oob_2048_ecc4;
                priv->bufnum_mask = 3;
                break;
 
        case CSOR_NAND_PGS_4K:
-               if ((csor & CSOR_NAND_ECC_MODE_MASK) ==
-                   CSOR_NAND_ECC_MODE_4) {
-                       layout = &oob_4096_ecc4;
-               } else {
-                       layout = &oob_4096_ecc8;
-                       chip->ecc.bytes = 16;
-                       chip->ecc.strength = 8;
-               }
-
                priv->bufnum_mask = 1;
                break;
 
        case CSOR_NAND_PGS_8K:
-               if ((csor & CSOR_NAND_ECC_MODE_MASK) ==
-                   CSOR_NAND_ECC_MODE_4) {
-                       layout = &oob_8192_ecc4;
-               } else {
-                       layout = &oob_8192_ecc8;
-                       chip->ecc.bytes = 16;
-                       chip->ecc.strength = 8;
-               }
-
                priv->bufnum_mask = 0;
-       break;
+               break;
 
        default:
                dev_err(priv->dev, "bad csor %#x: bad page size\n", csor);
@@ -980,9 +882,20 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
        /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */
        if (csor & CSOR_NAND_ECC_DEC_EN) {
                chip->ecc.mode = NAND_ECC_HW;
-               chip->ecc.layout = layout;
+               mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops);
+
+               /* Hardware generates ECC per 512 Bytes */
+               chip->ecc.size = 512;
+               if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) {
+                       chip->ecc.bytes = 8;
+                       chip->ecc.strength = 4;
+               } else {
+                       chip->ecc.bytes = 16;
+                       chip->ecc.strength = 8;
+               }
        } else {
                chip->ecc.mode = NAND_ECC_SOFT;
+               chip->ecc.algo = NAND_ECC_HAMMING;
        }
 
        if (ctrl->version == FSL_IFC_VERSION_1_1_0)
@@ -1007,10 +920,10 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
        return 0;
 }
 
-static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank,
+static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank,
                      phys_addr_t addr)
 {
-       u32 cspr = ifc_in32(&ifc->cspr_cs[bank].cspr);
+       u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr);
 
        if (!(cspr & CSPR_V))
                return 0;
@@ -1024,7 +937,7 @@ static DEFINE_MUTEX(fsl_ifc_nand_mutex);
 
 static int fsl_ifc_nand_probe(struct platform_device *dev)
 {
-       struct fsl_ifc_regs __iomem *ifc;
+       struct fsl_ifc_runtime __iomem *ifc;
        struct fsl_ifc_mtd *priv;
        struct resource res;
        static const char *part_probe_types[]
@@ -1034,9 +947,9 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
        struct device_node *node = dev->dev.of_node;
        struct mtd_info *mtd;
 
-       if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs)
+       if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs)
                return -ENODEV;
-       ifc = fsl_ifc_ctrl_dev->regs;
+       ifc = fsl_ifc_ctrl_dev->rregs;
 
        /* get, allocate and map the memory resource */
        ret = of_address_to_resource(node, 0, &res);
@@ -1047,7 +960,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
 
        /* find which chip select it is connected to */
        for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) {
-               if (match_bank(ifc, bank, res.start))
+               if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start))
                        break;
        }
 
index cafd12d..d85fa25 100644 (file)
@@ -170,6 +170,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
        fun->chip.read_buf = fun_read_buf;
        fun->chip.write_buf = fun_write_buf;
        fun->chip.ecc.mode = NAND_ECC_SOFT;
+       fun->chip.ecc.algo = NAND_ECC_HAMMING;
        if (fun->mchip_count > 1)
                fun->chip.select_chip = fun_select_chip;
 
index 1bdcd4f..d4f454a 100644 (file)
 #include <linux/amba/bus.h>
 #include <mtd/mtd-abi.h>
 
-static struct nand_ecclayout fsmc_ecc1_128_layout = {
-       .eccbytes = 24,
-       .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52,
-               66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116},
-       .oobfree = {
-               {.offset = 8, .length = 8},
-               {.offset = 24, .length = 8},
-               {.offset = 40, .length = 8},
-               {.offset = 56, .length = 8},
-               {.offset = 72, .length = 8},
-               {.offset = 88, .length = 8},
-               {.offset = 104, .length = 8},
-               {.offset = 120, .length = 8}
-       }
-};
+static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
 
-static struct nand_ecclayout fsmc_ecc1_64_layout = {
-       .eccbytes = 12,
-       .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52},
-       .oobfree = {
-               {.offset = 8, .length = 8},
-               {.offset = 24, .length = 8},
-               {.offset = 40, .length = 8},
-               {.offset = 56, .length = 8},
-       }
-};
+       if (section >= chip->ecc.steps)
+               return -ERANGE;
 
-static struct nand_ecclayout fsmc_ecc1_16_layout = {
-       .eccbytes = 3,
-       .eccpos = {2, 3, 4},
-       .oobfree = {
-               {.offset = 8, .length = 8},
-       }
-};
+       oobregion->offset = (section * 16) + 2;
+       oobregion->length = 3;
 
-/*
- * ECC4 layout for NAND of pagesize 8192 bytes & OOBsize 256 bytes. 13*16 bytes
- * of OB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 46
- * bytes are free for use.
- */
-static struct nand_ecclayout fsmc_ecc4_256_layout = {
-       .eccbytes = 208,
-       .eccpos = {  2,   3,   4,   5,   6,   7,   8,
-               9,  10,  11,  12,  13,  14,
-               18,  19,  20,  21,  22,  23,  24,
-               25,  26,  27,  28,  29,  30,
-               34,  35,  36,  37,  38,  39,  40,
-               41,  42,  43,  44,  45,  46,
-               50,  51,  52,  53,  54,  55,  56,
-               57,  58,  59,  60,  61,  62,
-               66,  67,  68,  69,  70,  71,  72,
-               73,  74,  75,  76,  77,  78,
-               82,  83,  84,  85,  86,  87,  88,
-               89,  90,  91,  92,  93,  94,
-               98,  99, 100, 101, 102, 103, 104,
-               105, 106, 107, 108, 109, 110,
-               114, 115, 116, 117, 118, 119, 120,
-               121, 122, 123, 124, 125, 126,
-               130, 131, 132, 133, 134, 135, 136,
-               137, 138, 139, 140, 141, 142,
-               146, 147, 148, 149, 150, 151, 152,
-               153, 154, 155, 156, 157, 158,
-               162, 163, 164, 165, 166, 167, 168,
-               169, 170, 171, 172, 173, 174,
-               178, 179, 180, 181, 182, 183, 184,
-               185, 186, 187, 188, 189, 190,
-               194, 195, 196, 197, 198, 199, 200,
-               201, 202, 203, 204, 205, 206,
-               210, 211, 212, 213, 214, 215, 216,
-               217, 218, 219, 220, 221, 222,
-               226, 227, 228, 229, 230, 231, 232,
-               233, 234, 235, 236, 237, 238,
-               242, 243, 244, 245, 246, 247, 248,
-               249, 250, 251, 252, 253, 254
-       },
-       .oobfree = {
-               {.offset = 15, .length = 3},
-               {.offset = 31, .length = 3},
-               {.offset = 47, .length = 3},
-               {.offset = 63, .length = 3},
-               {.offset = 79, .length = 3},
-               {.offset = 95, .length = 3},
-               {.offset = 111, .length = 3},
-               {.offset = 127, .length = 3},
-               {.offset = 143, .length = 3},
-               {.offset = 159, .length = 3},
-               {.offset = 175, .length = 3},
-               {.offset = 191, .length = 3},
-               {.offset = 207, .length = 3},
-               {.offset = 223, .length = 3},
-               {.offset = 239, .length = 3},
-               {.offset = 255, .length = 1}
-       }
-};
+       return 0;
+}
 
-/*
- * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes
- * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118
- * bytes are free for use.
- */
-static struct nand_ecclayout fsmc_ecc4_224_layout = {
-       .eccbytes = 104,
-       .eccpos = {  2,   3,   4,   5,   6,   7,   8,
-               9,  10,  11,  12,  13,  14,
-               18,  19,  20,  21,  22,  23,  24,
-               25,  26,  27,  28,  29,  30,
-               34,  35,  36,  37,  38,  39,  40,
-               41,  42,  43,  44,  45,  46,
-               50,  51,  52,  53,  54,  55,  56,
-               57,  58,  59,  60,  61,  62,
-               66,  67,  68,  69,  70,  71,  72,
-               73,  74,  75,  76,  77,  78,
-               82,  83,  84,  85,  86,  87,  88,
-               89,  90,  91,  92,  93,  94,
-               98,  99, 100, 101, 102, 103, 104,
-               105, 106, 107, 108, 109, 110,
-               114, 115, 116, 117, 118, 119, 120,
-               121, 122, 123, 124, 125, 126
-       },
-       .oobfree = {
-               {.offset = 15, .length = 3},
-               {.offset = 31, .length = 3},
-               {.offset = 47, .length = 3},
-               {.offset = 63, .length = 3},
-               {.offset = 79, .length = 3},
-               {.offset = 95, .length = 3},
-               {.offset = 111, .length = 3},
-               {.offset = 127, .length = 97}
-       }
-};
+static int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section,
+                                   struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
 
-/*
- * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 128 bytes. 13*8 bytes
- * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 22
- * bytes are free for use.
- */
-static struct nand_ecclayout fsmc_ecc4_128_layout = {
-       .eccbytes = 104,
-       .eccpos = {  2,   3,   4,   5,   6,   7,   8,
-               9,  10,  11,  12,  13,  14,
-               18,  19,  20,  21,  22,  23,  24,
-               25,  26,  27,  28,  29,  30,
-               34,  35,  36,  37,  38,  39,  40,
-               41,  42,  43,  44,  45,  46,
-               50,  51,  52,  53,  54,  55,  56,
-               57,  58,  59,  60,  61,  62,
-               66,  67,  68,  69,  70,  71,  72,
-               73,  74,  75,  76,  77,  78,
-               82,  83,  84,  85,  86,  87,  88,
-               89,  90,  91,  92,  93,  94,
-               98,  99, 100, 101, 102, 103, 104,
-               105, 106, 107, 108, 109, 110,
-               114, 115, 116, 117, 118, 119, 120,
-               121, 122, 123, 124, 125, 126
-       },
-       .oobfree = {
-               {.offset = 15, .length = 3},
-               {.offset = 31, .length = 3},
-               {.offset = 47, .length = 3},
-               {.offset = 63, .length = 3},
-               {.offset = 79, .length = 3},
-               {.offset = 95, .length = 3},
-               {.offset = 111, .length = 3},
-               {.offset = 127, .length = 1}
-       }
-};
+       if (section >= chip->ecc.steps)
+               return -ERANGE;
 
-/*
- * ECC4 layout for NAND of pagesize 2048 bytes & OOBsize 64 bytes. 13*4 bytes of
- * OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 10
- * bytes are free for use.
- */
-static struct nand_ecclayout fsmc_ecc4_64_layout = {
-       .eccbytes = 52,
-       .eccpos = {  2,   3,   4,   5,   6,   7,   8,
-               9,  10,  11,  12,  13,  14,
-               18,  19,  20,  21,  22,  23,  24,
-               25,  26,  27,  28,  29,  30,
-               34,  35,  36,  37,  38,  39,  40,
-               41,  42,  43,  44,  45,  46,
-               50,  51,  52,  53,  54,  55,  56,
-               57,  58,  59,  60,  61,  62,
-       },
-       .oobfree = {
-               {.offset = 15, .length = 3},
-               {.offset = 31, .length = 3},
-               {.offset = 47, .length = 3},
-               {.offset = 63, .length = 1},
-       }
-};
+       oobregion->offset = (section * 16) + 8;
 
-/*
- * ECC4 layout for NAND of pagesize 512 bytes & OOBsize 16 bytes. 13 bytes of
- * OOB size is reserved for ECC, Byte no. 4 & 5 reserved for bad block and One
- * byte is free for use.
- */
-static struct nand_ecclayout fsmc_ecc4_16_layout = {
-       .eccbytes = 13,
-       .eccpos = { 0,  1,  2,  3,  6,  7, 8,
-               9, 10, 11, 12, 13, 14
-       },
-       .oobfree = {
-               {.offset = 15, .length = 1},
-       }
+       if (section < chip->ecc.steps - 1)
+               oobregion->length = 8;
+       else
+               oobregion->length = mtd->oobsize - oobregion->offset;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = {
+       .ecc = fsmc_ecc1_ooblayout_ecc,
+       .free = fsmc_ecc1_ooblayout_free,
 };
 
 /*
@@ -250,28 +81,46 @@ static struct nand_ecclayout fsmc_ecc4_16_layout = {
  * There are 13 bytes of ecc for every 512 byte block and it has to be read
  * consecutively and immediately after the 512 byte data block for hardware to
  * generate the error bit offsets in 512 byte data.
- * Managing the ecc bytes in the following way makes it easier for software to
- * read ecc bytes consecutive to data bytes. This way is similar to
- * oobfree structure maintained already in generic nand driver
  */
-static struct fsmc_eccplace fsmc_ecc4_lp_place = {
-       .eccplace = {
-               {.offset = 2, .length = 13},
-               {.offset = 18, .length = 13},
-               {.offset = 34, .length = 13},
-               {.offset = 50, .length = 13},
-               {.offset = 66, .length = 13},
-               {.offset = 82, .length = 13},
-               {.offset = 98, .length = 13},
-               {.offset = 114, .length = 13}
-       }
-};
+static int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
 
-static struct fsmc_eccplace fsmc_ecc4_sp_place = {
-       .eccplace = {
-               {.offset = 0, .length = 4},
-               {.offset = 6, .length = 9}
-       }
+       if (section >= chip->ecc.steps)
+               return -ERANGE;
+
+       oobregion->length = chip->ecc.bytes;
+
+       if (!section && mtd->writesize <= 512)
+               oobregion->offset = 0;
+       else
+               oobregion->offset = (section * 16) + 2;
+
+       return 0;
+}
+
+static int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section,
+                                   struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section >= chip->ecc.steps)
+               return -ERANGE;
+
+       oobregion->offset = (section * 16) + 15;
+
+       if (section < chip->ecc.steps - 1)
+               oobregion->length = 3;
+       else
+               oobregion->length = mtd->oobsize - oobregion->offset;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = {
+       .ecc = fsmc_ecc4_ooblayout_ecc,
+       .free = fsmc_ecc4_ooblayout_free,
 };
 
 /**
@@ -283,7 +132,6 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = {
  * @partitions:                Partition info for a NAND Flash.
  * @nr_partitions:     Total number of partition of a NAND flash.
  *
- * @ecc_place:         ECC placing locations in oobfree type format.
  * @bank:              Bank number for probed device.
  * @clk:               Clock structure for FSMC.
  *
@@ -303,7 +151,6 @@ struct fsmc_nand_data {
        struct mtd_partition    *partitions;
        unsigned int            nr_partitions;
 
-       struct fsmc_eccplace    *ecc_place;
        unsigned int            bank;
        struct device           *dev;
        enum access_mode        mode;
@@ -710,8 +557,6 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
 static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                                 uint8_t *buf, int oob_required, int page)
 {
-       struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
-       struct fsmc_eccplace *ecc_place = host->ecc_place;
        int i, j, s, stat, eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
        int eccsteps = chip->ecc.steps;
@@ -734,9 +579,15 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                chip->read_buf(mtd, p, eccsize);
 
                for (j = 0; j < eccbytes;) {
-                       off = ecc_place->eccplace[group].offset;
-                       len = ecc_place->eccplace[group].length;
-                       group++;
+                       struct mtd_oob_region oobregion;
+                       int ret;
+
+                       ret = mtd_ooblayout_ecc(mtd, group++, &oobregion);
+                       if (ret)
+                               return ret;
+
+                       off = oobregion.offset;
+                       len = oobregion.length;
 
                        /*
                         * length is intentionally kept a higher multiple of 2
@@ -1084,24 +935,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
        if (AMBA_REV_BITS(host->pid) >= 8) {
                switch (mtd->oobsize) {
                case 16:
-                       nand->ecc.layout = &fsmc_ecc4_16_layout;
-                       host->ecc_place = &fsmc_ecc4_sp_place;
-                       break;
                case 64:
-                       nand->ecc.layout = &fsmc_ecc4_64_layout;
-                       host->ecc_place = &fsmc_ecc4_lp_place;
-                       break;
                case 128:
-                       nand->ecc.layout = &fsmc_ecc4_128_layout;
-                       host->ecc_place = &fsmc_ecc4_lp_place;
-                       break;
                case 224:
-                       nand->ecc.layout = &fsmc_ecc4_224_layout;
-                       host->ecc_place = &fsmc_ecc4_lp_place;
-                       break;
                case 256:
-                       nand->ecc.layout = &fsmc_ecc4_256_layout;
-                       host->ecc_place = &fsmc_ecc4_lp_place;
                        break;
                default:
                        dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n",
@@ -1109,6 +946,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
                        ret = -EINVAL;
                        goto err_probe;
                }
+
+               mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops);
        } else {
                switch (nand->ecc.mode) {
                case NAND_ECC_HW:
@@ -1119,9 +958,11 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
                        nand->ecc.strength = 1;
                        break;
 
-               case NAND_ECC_SOFT_BCH:
-                       dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n");
-                       break;
+               case NAND_ECC_SOFT:
+                       if (nand->ecc.algo == NAND_ECC_BCH) {
+                               dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n");
+                               break;
+                       }
 
                default:
                        dev_err(&pdev->dev, "Unsupported ECC mode!\n");
@@ -1132,16 +973,13 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
                 * Don't set layout for BCH4 SW ECC. This will be
                 * generated later in nand_bch_init() later.
                 */
-               if (nand->ecc.mode != NAND_ECC_SOFT_BCH) {
+               if (nand->ecc.mode == NAND_ECC_HW) {
                        switch (mtd->oobsize) {
                        case 16:
-                               nand->ecc.layout = &fsmc_ecc1_16_layout;
-                               break;
                        case 64:
-                               nand->ecc.layout = &fsmc_ecc1_64_layout;
-                               break;
                        case 128:
-                               nand->ecc.layout = &fsmc_ecc1_128_layout;
+                               mtd_set_ooblayout(mtd,
+                                                 &fsmc_ecc1_ooblayout_ops);
                                break;
                        default:
                                dev_warn(&pdev->dev,
index ded658f..6317f68 100644 (file)
@@ -273,6 +273,7 @@ static int gpio_nand_probe(struct platform_device *pdev)
        nand_set_flash_node(chip, pdev->dev.of_node);
        chip->IO_ADDR_W         = chip->IO_ADDR_R;
        chip->ecc.mode          = NAND_ECC_SOFT;
+       chip->ecc.algo          = NAND_ECC_HAMMING;
        chip->options           = gpiomtd->plat.options;
        chip->chip_delay        = gpiomtd->plat.chip_delay;
        chip->cmd_ctrl          = gpio_nand_cmd_ctrl;
index 8122c69..6e46156 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/mtd/partitions.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/of_mtd.h>
 #include "gpmi-nand.h"
 #include "bch-regs.h"
 
@@ -47,10 +46,44 @@ static struct nand_bbt_descr gpmi_bbt_descr = {
  * We may change the layout if we can get the ECC info from the datasheet,
  * else we will use all the (page + OOB).
  */
-static struct nand_ecclayout gpmi_hw_ecclayout = {
-       .eccbytes = 0,
-       .eccpos = { 0, },
-       .oobfree = { {.offset = 0, .length = 0} }
+static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section,
+                             struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct gpmi_nand_data *this = nand_get_controller_data(chip);
+       struct bch_geometry *geo = &this->bch_geometry;
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 0;
+       oobregion->length = geo->page_size - mtd->writesize;
+
+       return 0;
+}
+
+static int gpmi_ooblayout_free(struct mtd_info *mtd, int section,
+                              struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct gpmi_nand_data *this = nand_get_controller_data(chip);
+       struct bch_geometry *geo = &this->bch_geometry;
+
+       if (section)
+               return -ERANGE;
+
+       /* The available oob size we have. */
+       if (geo->page_size < mtd->writesize + mtd->oobsize) {
+               oobregion->offset = geo->page_size - mtd->writesize;
+               oobregion->length = mtd->oobsize - oobregion->offset;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = {
+       .ecc = gpmi_ooblayout_ecc,
+       .free = gpmi_ooblayout_free,
 };
 
 static const struct gpmi_devdata gpmi_devdata_imx23 = {
@@ -141,7 +174,6 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
        struct bch_geometry *geo = &this->bch_geometry;
        struct nand_chip *chip = &this->nand;
        struct mtd_info *mtd = nand_to_mtd(chip);
-       struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree;
        unsigned int block_mark_bit_offset;
 
        if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
@@ -229,12 +261,6 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
        geo->page_size = mtd->writesize + geo->metadata_size +
                (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
 
-       /* The available oob size we have. */
-       if (geo->page_size < mtd->writesize + mtd->oobsize) {
-               of->offset = geo->page_size - mtd->writesize;
-               of->length = mtd->oobsize - of->offset;
-       }
-
        geo->payload_size = mtd->writesize;
 
        geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
@@ -797,6 +823,7 @@ static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
 
        this->cmd_buffer        = NULL;
        this->data_buffer_dma   = NULL;
+       this->raw_buffer        = NULL;
        this->page_buffer_virt  = NULL;
        this->page_buffer_size  =  0;
 }
@@ -1037,14 +1064,87 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
        /* Loop over status bytes, accumulating ECC status. */
        status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
 
+       read_page_swap_end(this, buf, nfc_geo->payload_size,
+                          this->payload_virt, this->payload_phys,
+                          nfc_geo->payload_size,
+                          payload_virt, payload_phys);
+
        for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
                if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
                        continue;
 
                if (*status == STATUS_UNCORRECTABLE) {
+                       int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
+                       u8 *eccbuf = this->raw_buffer;
+                       int offset, bitoffset;
+                       int eccbytes;
+                       int flips;
+
+                       /* Read ECC bytes into our internal raw_buffer */
+                       offset = nfc_geo->metadata_size * 8;
+                       offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1);
+                       offset -= eccbits;
+                       bitoffset = offset % 8;
+                       eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
+                       offset /= 8;
+                       eccbytes -= offset;
+                       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+                       chip->read_buf(mtd, eccbuf, eccbytes);
+
+                       /*
+                        * ECC data are not byte aligned and we may have
+                        * in-band data in the first and last byte of
+                        * eccbuf. Set non-eccbits to one so that
+                        * nand_check_erased_ecc_chunk() does not count them
+                        * as bitflips.
+                        */
+                       if (bitoffset)
+                               eccbuf[0] |= GENMASK(bitoffset - 1, 0);
+
+                       bitoffset = (bitoffset + eccbits) % 8;
+                       if (bitoffset)
+                               eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset);
+
+                       /*
+                        * The ECC hardware has an uncorrectable ECC status
+                        * code in case we have bitflips in an erased page. As
+                        * nothing was written into this subpage the ECC is
+                        * obviously wrong and we can not trust it. We assume
+                        * at this point that we are reading an erased page and
+                        * try to correct the bitflips in buffer up to
+                        * ecc_strength bitflips. If this is a page with random
+                        * data, we exceed this number of bitflips and have a
+                        * ECC failure. Otherwise we use the corrected buffer.
+                        */
+                       if (i == 0) {
+                               /* The first block includes metadata */
+                               flips = nand_check_erased_ecc_chunk(
+                                               buf + i * nfc_geo->ecc_chunk_size,
+                                               nfc_geo->ecc_chunk_size,
+                                               eccbuf, eccbytes,
+                                               auxiliary_virt,
+                                               nfc_geo->metadata_size,
+                                               nfc_geo->ecc_strength);
+                       } else {
+                               flips = nand_check_erased_ecc_chunk(
+                                               buf + i * nfc_geo->ecc_chunk_size,
+                                               nfc_geo->ecc_chunk_size,
+                                               eccbuf, eccbytes,
+                                               NULL, 0,
+                                               nfc_geo->ecc_strength);
+                       }
+
+                       if (flips > 0) {
+                               max_bitflips = max_t(unsigned int, max_bitflips,
+                                                    flips);
+                               mtd->ecc_stats.corrected += flips;
+                               continue;
+                       }
+
                        mtd->ecc_stats.failed++;
                        continue;
                }
+
                mtd->ecc_stats.corrected += *status;
                max_bitflips = max_t(unsigned int, max_bitflips, *status);
        }
@@ -1064,11 +1164,6 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
                chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
        }
 
-       read_page_swap_end(this, buf, nfc_geo->payload_size,
-                       this->payload_virt, this->payload_phys,
-                       nfc_geo->payload_size,
-                       payload_virt, payload_phys);
-
        return max_bitflips;
 }
 
@@ -1327,18 +1422,19 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
 static int
 gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
 {
-       struct nand_oobfree *of = mtd->ecclayout->oobfree;
+       struct mtd_oob_region of = { };
        int status = 0;
 
        /* Do we have available oob area? */
-       if (!of->length)
+       mtd_ooblayout_free(mtd, 0, &of);
+       if (!of.length)
                return -EPERM;
 
        if (!nand_is_slc(chip))
                return -EPERM;
 
-       chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of->offset, page);
-       chip->write_buf(mtd, chip->oob_poi + of->offset, of->length);
+       chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of.offset, page);
+       chip->write_buf(mtd, chip->oob_poi + of.offset, of.length);
        chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
 
        status = chip->waitfunc(mtd, chip);
@@ -1840,6 +1936,7 @@ static void gpmi_nand_exit(struct gpmi_nand_data *this)
 static int gpmi_init_last(struct gpmi_nand_data *this)
 {
        struct nand_chip *chip = &this->nand;
+       struct mtd_info *mtd = nand_to_mtd(chip);
        struct nand_ecc_ctrl *ecc = &chip->ecc;
        struct bch_geometry *bch_geo = &this->bch_geometry;
        int ret;
@@ -1861,7 +1958,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
        ecc->mode       = NAND_ECC_HW;
        ecc->size       = bch_geo->ecc_chunk_size;
        ecc->strength   = bch_geo->ecc_strength;
-       ecc->layout     = &gpmi_hw_ecclayout;
+       mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops);
 
        /*
         * We only enable the subpage read when:
@@ -1914,16 +2011,6 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
        /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
        this->swap_block_mark = !GPMI_IS_MX23(this);
 
-       if (of_get_nand_on_flash_bbt(this->dev->of_node)) {
-               chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
-
-               if (of_property_read_bool(this->dev->of_node,
-                                               "fsl,no-blockmark-swap"))
-                       this->swap_block_mark = false;
-       }
-       dev_dbg(this->dev, "Blockmark swapping %sabled\n",
-               this->swap_block_mark ? "en" : "dis");
-
        /*
         * Allocate a temporary DMA buffer for reading ID in the
         * nand_scan_ident().
@@ -1938,6 +2025,16 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
        if (ret)
                goto err_out;
 
+       if (chip->bbt_options & NAND_BBT_USE_FLASH) {
+               chip->bbt_options |= NAND_BBT_NO_OOB;
+
+               if (of_property_read_bool(this->dev->of_node,
+                                               "fsl,no-blockmark-swap"))
+                       this->swap_block_mark = false;
+       }
+       dev_dbg(this->dev, "Blockmark swapping %sabled\n",
+               this->swap_block_mark ? "en" : "dis");
+
        ret = gpmi_init_last(this);
        if (ret)
                goto err_out;
index 96502b6..9432546 100644 (file)
@@ -19,7 +19,6 @@
  * GNU General Public License for more details.
  */
 #include <linux/of.h>
-#include <linux/of_mtd.h>
 #include <linux/mtd/mtd.h>
 #include <linux/sizes.h>
 #include <linux/clk.h>
@@ -631,8 +630,28 @@ static void hisi_nfc_host_init(struct hinfc_host *host)
        hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN);
 }
 
-static struct nand_ecclayout nand_ecc_2K_16bits = {
-       .oobfree = { {2, 6} },
+static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section,
+                             struct mtd_oob_region *oobregion)
+{
+       /* FIXME: add ECC bytes position */
+       return -ENOTSUPP;
+}
+
+static int hisi_ooblayout_free(struct mtd_info *mtd, int section,
+                              struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 2;
+       oobregion->length = 6;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops hisi_ooblayout_ops = {
+       .ecc = hisi_ooblayout_ecc,
+       .free = hisi_ooblayout_free,
 };
 
 static int hisi_nfc_ecc_probe(struct hinfc_host *host)
@@ -642,10 +661,9 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host)
        struct device *dev = host->dev;
        struct nand_chip *chip = &host->chip;
        struct mtd_info *mtd = nand_to_mtd(chip);
-       struct device_node *np = host->dev->of_node;
 
-       size = of_get_nand_ecc_step_size(np);
-       strength = of_get_nand_ecc_strength(np);
+       size = chip->ecc.size;
+       strength = chip->ecc.strength;
        if (size != 1024) {
                dev_err(dev, "error ecc size: %d\n", size);
                return -EINVAL;
@@ -668,7 +686,7 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host)
        case 16:
                ecc_bits = 6;
                if (mtd->writesize == 2048)
-                       chip->ecc.layout = &nand_ecc_2K_16bits;
+                       mtd_set_ooblayout(mtd, &hisi_ooblayout_ops);
 
                /* TODO: add more page size support */
                break;
@@ -695,7 +713,7 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host)
 
 static int hisi_nfc_probe(struct platform_device *pdev)
 {
-       int ret = 0, irq, buswidth, flag, max_chips = HINFC504_MAX_CHIP;
+       int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP;
        struct device *dev = &pdev->dev;
        struct hinfc_host *host;
        struct nand_chip  *chip;
@@ -747,12 +765,6 @@ static int hisi_nfc_probe(struct platform_device *pdev)
        chip->read_buf          = hisi_nfc_read_buf;
        chip->chip_delay        = HINFC504_CHIP_DELAY;
 
-       chip->ecc.mode = of_get_nand_ecc_mode(np);
-
-       buswidth = of_get_nand_bus_width(np);
-       if (buswidth == 16)
-               chip->options |= NAND_BUSWIDTH_16;
-
        hisi_nfc_host_init(host);
 
        ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host);
index 673ceb2..5551c36 100644 (file)
@@ -221,7 +221,6 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
        struct jz_nand *nand = mtd_to_jz_nand(mtd);
        int i, error_count, index;
        uint32_t reg, status, error;
-       uint32_t t;
        unsigned int timeout = 1000;
 
        for (i = 0; i < 9; ++i)
@@ -476,7 +475,7 @@ static int jz_nand_probe(struct platform_device *pdev)
        }
 
        if (pdata && pdata->ident_callback) {
-               pdata->ident_callback(pdev, chip, &pdata->partitions,
+               pdata->ident_callback(pdev, mtd, &pdata->partitions,
                                        &pdata->num_partitions);
        }
 
index 755499c..d74f4ba 100644 (file)
@@ -287,7 +287,6 @@ static struct jz4780_bch *jz4780_bch_get(struct device_node *np)
        bch = platform_get_drvdata(pdev);
        clk_prepare_enable(bch->clk);
 
-       bch->dev = &pdev->dev;
        return bch;
 }
 
index e1c016c..daf3c42 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/gpio/consumer.h>
-#include <linux/of_mtd.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
@@ -56,8 +55,6 @@ struct jz4780_nand_chip {
        struct nand_chip chip;
        struct list_head chip_list;
 
-       struct nand_ecclayout ecclayout;
-
        struct gpio_desc *busy_gpio;
        struct gpio_desc *wp_gpio;
        unsigned int reading: 1;
@@ -165,8 +162,7 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
        struct nand_chip *chip = &nand->chip;
        struct mtd_info *mtd = nand_to_mtd(chip);
        struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller);
-       struct nand_ecclayout *layout = &nand->ecclayout;
-       u32 start, i;
+       int eccbytes;
 
        chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) *
                                (chip->ecc.strength / 8);
@@ -183,7 +179,6 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
                chip->ecc.correct = jz4780_nand_ecc_correct;
                /* fall through */
        case NAND_ECC_SOFT:
-       case NAND_ECC_SOFT_BCH:
                dev_info(dev, "using %s (strength %d, size %d, bytes %d)\n",
                        (nfc->bch) ? "hardware BCH" : "software ECC",
                        chip->ecc.strength, chip->ecc.size, chip->ecc.bytes);
@@ -201,23 +196,17 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
                return 0;
 
        /* Generate ECC layout. ECC codes are right aligned in the OOB area. */
-       layout->eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes;
+       eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes;
 
-       if (layout->eccbytes > mtd->oobsize - 2) {
+       if (eccbytes > mtd->oobsize - 2) {
                dev_err(dev,
                        "invalid ECC config: required %d ECC bytes, but only %d are available",
-                       layout->eccbytes, mtd->oobsize - 2);
+                       eccbytes, mtd->oobsize - 2);
                return -EINVAL;
        }
 
-       start = mtd->oobsize - layout->eccbytes;
-       for (i = 0; i < layout->eccbytes; i++)
-               layout->eccpos[i] = start + i;
-
-       layout->oobfree[0].offset = 2;
-       layout->oobfree[0].length = mtd->oobsize - layout->eccbytes - 2;
+       mtd->ooblayout = &nand_ooblayout_lp_ops;
 
-       chip->ecc.layout = layout;
        return 0;
 }
 
index d8c3e7a..8523881 100644 (file)
@@ -35,7 +35,6 @@
 #include <linux/completion.h>
 #include <linux/interrupt.h>
 #include <linux/of.h>
-#include <linux/of_mtd.h>
 #include <linux/of_gpio.h>
 #include <linux/mtd/lpc32xx_mlc.h>
 #include <linux/io.h>
@@ -139,22 +138,37 @@ struct lpc32xx_nand_cfg_mlc {
        unsigned num_parts;
 };
 
-static struct nand_ecclayout lpc32xx_nand_oob = {
-       .eccbytes = 40,
-       .eccpos = { 6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
-                  22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-                  38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-                  54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
-       .oobfree = {
-               { .offset = 0,
-                 .length = 6, },
-               { .offset = 16,
-                 .length = 6, },
-               { .offset = 32,
-                 .length = 6, },
-               { .offset = 48,
-                 .length = 6, },
-               },
+static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *nand_chip = mtd_to_nand(mtd);
+
+       if (section >= nand_chip->ecc.steps)
+               return -ERANGE;
+
+       oobregion->offset = ((section + 1) * 16) - nand_chip->ecc.bytes;
+       oobregion->length = nand_chip->ecc.bytes;
+
+       return 0;
+}
+
+static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *nand_chip = mtd_to_nand(mtd);
+
+       if (section >= nand_chip->ecc.steps)
+               return -ERANGE;
+
+       oobregion->offset = 16 * section;
+       oobregion->length = 16 - nand_chip->ecc.bytes;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = {
+       .ecc = lpc32xx_ooblayout_ecc,
+       .free = lpc32xx_ooblayout_free,
 };
 
 static struct nand_bbt_descr lpc32xx_nand_bbt = {
@@ -713,6 +727,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
        nand_chip->ecc.write_oob = lpc32xx_write_oob;
        nand_chip->ecc.read_oob = lpc32xx_read_oob;
        nand_chip->ecc.strength = 4;
+       nand_chip->ecc.bytes = 10;
        nand_chip->waitfunc = lpc32xx_waitfunc;
 
        nand_chip->options = NAND_NO_SUBPAGE_WRITE;
@@ -751,7 +766,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
 
        nand_chip->ecc.mode = NAND_ECC_HW;
        nand_chip->ecc.size = 512;
-       nand_chip->ecc.layout = &lpc32xx_nand_oob;
+       mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
        host->mlcsubpages = mtd->writesize / 512;
 
        /* initially clear interrupt status */
index 3b8f373..8d3edc3 100644 (file)
@@ -35,7 +35,6 @@
 #include <linux/mtd/nand_ecc.h>
 #include <linux/gpio.h>
 #include <linux/of.h>
-#include <linux/of_mtd.h>
 #include <linux/of_gpio.h>
 #include <linux/mtd/lpc32xx_slc.h>
 
  * NAND ECC Layout for small page NAND devices
  * Note: For large and huge page devices, the default layouts are used
  */
-static struct nand_ecclayout lpc32xx_nand_oob_16 = {
-       .eccbytes = 6,
-       .eccpos = {10, 11, 12, 13, 14, 15},
-       .oobfree = {
-               { .offset = 0, .length = 4 },
-               { .offset = 6, .length = 4 },
-       },
+static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->length = 6;
+       oobregion->offset = 10;
+
+       return 0;
+}
+
+static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       if (section > 1)
+               return -ERANGE;
+
+       if (!section) {
+               oobregion->offset = 0;
+               oobregion->length = 4;
+       } else {
+               oobregion->offset = 6;
+               oobregion->length = 4;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = {
+       .ecc = lpc32xx_ooblayout_ecc,
+       .free = lpc32xx_ooblayout_free,
 };
 
 static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
@@ -194,7 +218,6 @@ struct lpc32xx_nand_cfg_slc {
        uint32_t rwidth;
        uint32_t rhold;
        uint32_t rsetup;
-       bool use_bbt;
        int wp_gpio;
        struct mtd_partition *parts;
        unsigned num_parts;
@@ -604,7 +627,8 @@ static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd,
                                           int oob_required, int page)
 {
        struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
-       int stat, i, status;
+       struct mtd_oob_region oobregion = { };
+       int stat, i, status, error;
        uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE];
 
        /* Issue read command */
@@ -620,7 +644,11 @@ static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd,
        lpc32xx_slc_ecc_copy(tmpecc, (uint32_t *) host->ecc_buf, chip->ecc.steps);
 
        /* Pointer to ECC data retrieved from NAND spare area */
-       oobecc = chip->oob_poi + chip->ecc.layout->eccpos[0];
+       error = mtd_ooblayout_ecc(mtd, 0, &oobregion);
+       if (error)
+               return error;
+
+       oobecc = chip->oob_poi + oobregion.offset;
 
        for (i = 0; i < chip->ecc.steps; i++) {
                stat = chip->ecc.correct(mtd, buf, oobecc,
@@ -666,7 +694,8 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,
                                            int oob_required, int page)
 {
        struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
-       uint8_t *pb = chip->oob_poi + chip->ecc.layout->eccpos[0];
+       struct mtd_oob_region oobregion = { };
+       uint8_t *pb;
        int error;
 
        /* Write data, calculate ECC on outbound data */
@@ -678,6 +707,11 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,
         * The calculated ECC needs some manual work done to it before
         * committing it to NAND. Process the calculated ECC and place
         * the resultant values directly into the OOB buffer. */
+       error = mtd_ooblayout_ecc(mtd, 0, &oobregion);
+       if (error)
+               return error;
+
+       pb = chip->oob_poi + oobregion.offset;
        lpc32xx_slc_ecc_copy(pb, (uint32_t *)host->ecc_buf, chip->ecc.steps);
 
        /* Write ECC data to device */
@@ -747,7 +781,6 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev)
                return NULL;
        }
 
-       ncfg->use_bbt = of_get_nand_on_flash_bbt(np);
        ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
 
        return ncfg;
@@ -875,26 +908,22 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
         * custom BBT marker layout.
         */
        if (mtd->writesize <= 512)
-               chip->ecc.layout = &lpc32xx_nand_oob_16;
+               mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
 
        /* These sizes remain the same regardless of page size */
        chip->ecc.size = 256;
        chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES;
        chip->ecc.prepad = chip->ecc.postpad = 0;
 
-       /* Avoid extra scan if using BBT, setup BBT support */
-       if (host->ncfg->use_bbt) {
-               chip->bbt_options |= NAND_BBT_USE_FLASH;
-
-               /*
-                * Use a custom BBT marker setup for small page FLASH that
-                * won't interfere with the ECC layout. Large and huge page
-                * FLASH use the standard layout.
-                */
-               if (mtd->writesize <= 512) {
-                       chip->bbt_td = &bbt_smallpage_main_descr;
-                       chip->bbt_md = &bbt_smallpage_mirror_descr;
-               }
+       /*
+        * Use a custom BBT marker setup for small page FLASH that
+        * won't interfere with the ECC layout. Large and huge page
+        * FLASH use the standard layout.
+        */
+       if ((chip->bbt_options & NAND_BBT_USE_FLASH) &&
+           mtd->writesize <= 512) {
+               chip->bbt_td = &bbt_smallpage_main_descr;
+               chip->bbt_md = &bbt_smallpage_mirror_descr;
        }
 
        /*
index 5d7843f..7eacb2f 100644 (file)
@@ -710,6 +710,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
        chip->select_chip = mpc5121_nfc_select_chip;
        chip->bbt_options = NAND_BBT_USE_FLASH;
        chip->ecc.mode = NAND_ECC_SOFT;
+       chip->ecc.algo = NAND_ECC_HAMMING;
 
        /* Support external chip-select logic on ADS5121 board */
        if (of_machine_is_compatible("fsl,mpc5121ads")) {
index 854c832..5173fad 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/completion.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/of_mtd.h>
 
 #include <asm/mach/flash.h>
 #include <linux/platform_data/mtd-mxc_nand.h>
@@ -149,7 +148,7 @@ struct mxc_nand_devtype_data {
        int (*check_int)(struct mxc_nand_host *);
        void (*irq_control)(struct mxc_nand_host *, int);
        u32 (*get_ecc_status)(struct mxc_nand_host *);
-       struct nand_ecclayout *ecclayout_512, *ecclayout_2k, *ecclayout_4k;
+       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);
@@ -200,73 +199,6 @@ struct mxc_nand_host {
        struct mxc_nand_platform_data pdata;
 };
 
-/* OOB placement block for use with hardware ecc generation */
-static struct nand_ecclayout nandv1_hw_eccoob_smallpage = {
-       .eccbytes = 5,
-       .eccpos = {6, 7, 8, 9, 10},
-       .oobfree = {{0, 5}, {12, 4}, }
-};
-
-static struct nand_ecclayout nandv1_hw_eccoob_largepage = {
-       .eccbytes = 20,
-       .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
-                  38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
-       .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, }
-};
-
-/* OOB description for 512 byte pages with 16 byte OOB */
-static struct nand_ecclayout nandv2_hw_eccoob_smallpage = {
-       .eccbytes = 1 * 9,
-       .eccpos = {
-                7,  8,  9, 10, 11, 12, 13, 14, 15
-       },
-       .oobfree = {
-               {.offset = 0, .length = 5}
-       }
-};
-
-/* OOB description for 2048 byte pages with 64 byte OOB */
-static struct nand_ecclayout nandv2_hw_eccoob_largepage = {
-       .eccbytes = 4 * 9,
-       .eccpos = {
-                7,  8,  9, 10, 11, 12, 13, 14, 15,
-               23, 24, 25, 26, 27, 28, 29, 30, 31,
-               39, 40, 41, 42, 43, 44, 45, 46, 47,
-               55, 56, 57, 58, 59, 60, 61, 62, 63
-       },
-       .oobfree = {
-               {.offset = 2, .length = 4},
-               {.offset = 16, .length = 7},
-               {.offset = 32, .length = 7},
-               {.offset = 48, .length = 7}
-       }
-};
-
-/* OOB description for 4096 byte pages with 128 byte OOB */
-static struct nand_ecclayout nandv2_hw_eccoob_4k = {
-       .eccbytes = 8 * 9,
-       .eccpos = {
-               7,  8,  9, 10, 11, 12, 13, 14, 15,
-               23, 24, 25, 26, 27, 28, 29, 30, 31,
-               39, 40, 41, 42, 43, 44, 45, 46, 47,
-               55, 56, 57, 58, 59, 60, 61, 62, 63,
-               71, 72, 73, 74, 75, 76, 77, 78, 79,
-               87, 88, 89, 90, 91, 92, 93, 94, 95,
-               103, 104, 105, 106, 107, 108, 109, 110, 111,
-               119, 120, 121, 122, 123, 124, 125, 126, 127,
-       },
-       .oobfree = {
-               {.offset = 2, .length = 4},
-               {.offset = 16, .length = 7},
-               {.offset = 32, .length = 7},
-               {.offset = 48, .length = 7},
-               {.offset = 64, .length = 7},
-               {.offset = 80, .length = 7},
-               {.offset = 96, .length = 7},
-               {.offset = 112, .length = 7},
-       }
-};
-
 static const char * const part_probes[] = {
        "cmdlinepart", "RedBoot", "ofpart", NULL };
 
@@ -942,6 +874,99 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
        }
 }
 
+static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
+                               struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *nand_chip = mtd_to_nand(mtd);
+
+       if (section >= nand_chip->ecc.steps)
+               return -ERANGE;
+
+       oobregion->offset = (section * 16) + 6;
+       oobregion->length = nand_chip->ecc.bytes;
+
+       return 0;
+}
+
+static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *nand_chip = mtd_to_nand(mtd);
+
+       if (section > nand_chip->ecc.steps)
+               return -ERANGE;
+
+       if (!section) {
+               if (mtd->writesize <= 512) {
+                       oobregion->offset = 0;
+                       oobregion->length = 5;
+               } else {
+                       oobregion->offset = 2;
+                       oobregion->length = 4;
+               }
+       } else {
+               oobregion->offset = ((section - 1) * 16) +
+                                   nand_chip->ecc.bytes + 6;
+               if (section < nand_chip->ecc.steps)
+                       oobregion->length = (section * 16) + 6 -
+                                           oobregion->offset;
+               else
+                       oobregion->length = mtd->oobsize - oobregion->offset;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = {
+       .ecc = mxc_v1_ooblayout_ecc,
+       .free = mxc_v1_ooblayout_free,
+};
+
+static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section,
+                               struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *nand_chip = mtd_to_nand(mtd);
+       int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
+
+       if (section >= nand_chip->ecc.steps)
+               return -ERANGE;
+
+       oobregion->offset = (section * stepsize) + 7;
+       oobregion->length = nand_chip->ecc.bytes;
+
+       return 0;
+}
+
+static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *nand_chip = mtd_to_nand(mtd);
+       int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
+
+       if (section > nand_chip->ecc.steps)
+               return -ERANGE;
+
+       if (!section) {
+               if (mtd->writesize <= 512) {
+                       oobregion->offset = 0;
+                       oobregion->length = 5;
+               } else {
+                       oobregion->offset = 2;
+                       oobregion->length = 4;
+               }
+       } else {
+               oobregion->offset = section * stepsize;
+               oobregion->length = 7;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = {
+       .ecc = mxc_v2_ooblayout_ecc,
+       .free = mxc_v2_ooblayout_free,
+};
+
 /*
  * v2 and v3 type controllers can do 4bit or 8bit ecc depending
  * on how much oob the nand chip has. For 8bit ecc we need at least
@@ -959,23 +984,6 @@ static int get_eccsize(struct mtd_info *mtd)
                return 8;
 }
 
-static void ecc_8bit_layout_4k(struct nand_ecclayout *layout)
-{
-       int i, j;
-
-       layout->eccbytes = 8*18;
-       for (i = 0; i < 8; i++)
-               for (j = 0; j < 18; j++)
-                       layout->eccpos[i*18 + j] = i*26 + j + 7;
-
-       layout->oobfree[0].offset = 2;
-       layout->oobfree[0].length = 4;
-       for (i = 1; i < 8; i++) {
-               layout->oobfree[i].offset = i*26;
-               layout->oobfree[i].length = 7;
-       }
-}
-
 static void preset_v1(struct mtd_info *mtd)
 {
        struct nand_chip *nand_chip = mtd_to_nand(mtd);
@@ -1269,9 +1277,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
        .check_int = check_int_v1_v2,
        .irq_control = irq_control_v1_v2,
        .get_ecc_status = get_ecc_status_v1,
-       .ecclayout_512 = &nandv1_hw_eccoob_smallpage,
-       .ecclayout_2k = &nandv1_hw_eccoob_largepage,
-       .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */
+       .ooblayout = &mxc_v1_ooblayout_ops,
        .select_chip = mxc_nand_select_chip_v1_v3,
        .correct_data = mxc_nand_correct_data_v1,
        .irqpending_quirk = 1,
@@ -1294,9 +1300,7 @@ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
        .check_int = check_int_v1_v2,
        .irq_control = irq_control_v1_v2,
        .get_ecc_status = get_ecc_status_v1,
-       .ecclayout_512 = &nandv1_hw_eccoob_smallpage,
-       .ecclayout_2k = &nandv1_hw_eccoob_largepage,
-       .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */
+       .ooblayout = &mxc_v1_ooblayout_ops,
        .select_chip = mxc_nand_select_chip_v1_v3,
        .correct_data = mxc_nand_correct_data_v1,
        .irqpending_quirk = 0,
@@ -1320,9 +1324,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
        .check_int = check_int_v1_v2,
        .irq_control = irq_control_v1_v2,
        .get_ecc_status = get_ecc_status_v2,
-       .ecclayout_512 = &nandv2_hw_eccoob_smallpage,
-       .ecclayout_2k = &nandv2_hw_eccoob_largepage,
-       .ecclayout_4k = &nandv2_hw_eccoob_4k,
+       .ooblayout = &mxc_v2_ooblayout_ops,
        .select_chip = mxc_nand_select_chip_v2,
        .correct_data = mxc_nand_correct_data_v2_v3,
        .irqpending_quirk = 0,
@@ -1346,9 +1348,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
        .check_int = check_int_v3,
        .irq_control = irq_control_v3,
        .get_ecc_status = get_ecc_status_v3,
-       .ecclayout_512 = &nandv2_hw_eccoob_smallpage,
-       .ecclayout_2k = &nandv2_hw_eccoob_largepage,
-       .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */
+       .ooblayout = &mxc_v2_ooblayout_ops,
        .select_chip = mxc_nand_select_chip_v1_v3,
        .correct_data = mxc_nand_correct_data_v2_v3,
        .irqpending_quirk = 0,
@@ -1373,9 +1373,7 @@ static const struct mxc_nand_devtype_data imx53_nand_devtype_data = {
        .check_int = check_int_v3,
        .irq_control = irq_control_v3,
        .get_ecc_status = get_ecc_status_v3,
-       .ecclayout_512 = &nandv2_hw_eccoob_smallpage,
-       .ecclayout_2k = &nandv2_hw_eccoob_largepage,
-       .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */
+       .ooblayout = &mxc_v2_ooblayout_ops,
        .select_chip = mxc_nand_select_chip_v1_v3,
        .correct_data = mxc_nand_correct_data_v2_v3,
        .irqpending_quirk = 0,
@@ -1461,25 +1459,12 @@ MODULE_DEVICE_TABLE(of, mxcnd_dt_ids);
 static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
 {
        struct device_node *np = host->dev->of_node;
-       struct mxc_nand_platform_data *pdata = &host->pdata;
        const struct of_device_id *of_id =
                of_match_device(mxcnd_dt_ids, host->dev);
-       int buswidth;
 
        if (!np)
                return 1;
 
-       if (of_get_nand_ecc_mode(np) >= 0)
-               pdata->hw_ecc = 1;
-
-       pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
-
-       buswidth = of_get_nand_bus_width(np);
-       if (buswidth < 0)
-               return buswidth;
-
-       pdata->width = buswidth / 8;
-
        host->devtype_data = of_id->data;
 
        return 0;
@@ -1576,27 +1561,22 @@ static int mxcnd_probe(struct platform_device *pdev)
 
        this->select_chip = host->devtype_data->select_chip;
        this->ecc.size = 512;
-       this->ecc.layout = host->devtype_data->ecclayout_512;
+       mtd_set_ooblayout(mtd, host->devtype_data->ooblayout);
 
        if (host->pdata.hw_ecc) {
-               this->ecc.calculate = mxc_nand_calculate_ecc;
-               this->ecc.hwctl = mxc_nand_enable_hwecc;
-               this->ecc.correct = host->devtype_data->correct_data;
                this->ecc.mode = NAND_ECC_HW;
        } else {
                this->ecc.mode = NAND_ECC_SOFT;
+               this->ecc.algo = NAND_ECC_HAMMING;
        }
 
        /* NAND bus width determines access functions used by upper layer */
        if (host->pdata.width == 2)
                this->options |= NAND_BUSWIDTH_16;
 
-       if (host->pdata.flash_bbt) {
-               this->bbt_td = &bbt_main_descr;
-               this->bbt_md = &bbt_mirror_descr;
-               /* update flash based bbt */
+       /* update flash based bbt */
+       if (host->pdata.flash_bbt)
                this->bbt_options |= NAND_BBT_USE_FLASH;
-       }
 
        init_completion(&host->op_completion);
 
@@ -1637,6 +1617,26 @@ static int mxcnd_probe(struct platform_device *pdev)
                goto escan;
        }
 
+       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;
+               break;
+
+       case NAND_ECC_SOFT:
+               break;
+
+       default:
+               err = -EINVAL;
+               goto escan;
+       }
+
+       if (this->bbt_options & NAND_BBT_USE_FLASH) {
+               this->bbt_td = &bbt_main_descr;
+               this->bbt_md = &bbt_mirror_descr;
+       }
+
        /* allocate the right size buffer now */
        devm_kfree(&pdev->dev, (void *)host->data_buf);
        host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize,
@@ -1649,12 +1649,11 @@ static int mxcnd_probe(struct platform_device *pdev)
        /* Call preset again, with correct writesize this time */
        host->devtype_data->preset(mtd);
 
-       if (mtd->writesize == 2048)
-               this->ecc.layout = host->devtype_data->ecclayout_2k;
-       else if (mtd->writesize == 4096) {
-               this->ecc.layout = host->devtype_data->ecclayout_4k;
-               if (get_eccsize(mtd) == 8)
-                       ecc_8bit_layout_4k(this->ecc.layout);
+       if (!this->ecc.bytes) {
+               if (host->eccsize == 8)
+                       this->ecc.bytes = 18;
+               else if (host->eccsize == 4)
+                       this->ecc.bytes = 9;
        }
 
        /*
index ba4f603..0b0dc29 100644 (file)
 #include <linux/bitops.h>
 #include <linux/io.h>
 #include <linux/mtd/partitions.h>
-#include <linux/of_mtd.h>
+#include <linux/of.h>
+
+static int nand_get_device(struct mtd_info *mtd, int new_state);
+
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+                            struct mtd_oob_ops *ops);
 
 /* Define default oob placement schemes for large and small page devices */
-static struct nand_ecclayout nand_oob_8 = {
-       .eccbytes = 3,
-       .eccpos = {0, 1, 2},
-       .oobfree = {
-               {.offset = 3,
-                .length = 2},
-               {.offset = 6,
-                .length = 2} }
-};
+static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
 
-static struct nand_ecclayout nand_oob_16 = {
-       .eccbytes = 6,
-       .eccpos = {0, 1, 2, 3, 6, 7},
-       .oobfree = {
-               {.offset = 8,
-                . length = 8} }
-};
+       if (section > 1)
+               return -ERANGE;
 
-static struct nand_ecclayout nand_oob_64 = {
-       .eccbytes = 24,
-       .eccpos = {
-                  40, 41, 42, 43, 44, 45, 46, 47,
-                  48, 49, 50, 51, 52, 53, 54, 55,
-                  56, 57, 58, 59, 60, 61, 62, 63},
-       .oobfree = {
-               {.offset = 2,
-                .length = 38} }
-};
+       if (!section) {
+               oobregion->offset = 0;
+               oobregion->length = 4;
+       } else {
+               oobregion->offset = 6;
+               oobregion->length = ecc->total - 4;
+       }
+
+       return 0;
+}
+
+static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       if (section > 1)
+               return -ERANGE;
+
+       if (mtd->oobsize == 16) {
+               if (section)
+                       return -ERANGE;
+
+               oobregion->length = 8;
+               oobregion->offset = 8;
+       } else {
+               oobregion->length = 2;
+               if (!section)
+                       oobregion->offset = 3;
+               else
+                       oobregion->offset = 6;
+       }
+
+       return 0;
+}
 
-static struct nand_ecclayout nand_oob_128 = {
-       .eccbytes = 48,
-       .eccpos = {
-                  80, 81, 82, 83, 84, 85, 86, 87,
-                  88, 89, 90, 91, 92, 93, 94, 95,
-                  96, 97, 98, 99, 100, 101, 102, 103,
-                  104, 105, 106, 107, 108, 109, 110, 111,
-                  112, 113, 114, 115, 116, 117, 118, 119,
-                  120, 121, 122, 123, 124, 125, 126, 127},
-       .oobfree = {
-               {.offset = 2,
-                .length = 78} }
+const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
+       .ecc = nand_ooblayout_ecc_sp,
+       .free = nand_ooblayout_free_sp,
 };
+EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops);
 
-static int nand_get_device(struct mtd_info *mtd, int new_state);
+static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
 
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
-                            struct mtd_oob_ops *ops);
+       if (section)
+               return -ERANGE;
+
+       oobregion->length = ecc->total;
+       oobregion->offset = mtd->oobsize - oobregion->length;
+
+       return 0;
+}
+
+static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->length = mtd->oobsize - ecc->total - 2;
+       oobregion->offset = 2;
+
+       return 0;
+}
+
+const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
+       .ecc = nand_ooblayout_ecc_lp,
+       .free = nand_ooblayout_free_lp,
+};
+EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
 
 static int check_offs_len(struct mtd_info *mtd,
                                        loff_t ofs, uint64_t len)
@@ -1279,13 +1321,12 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
 static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
                                uint8_t *buf, int oob_required, int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        int eccbytes = chip->ecc.bytes;
        int eccsteps = chip->ecc.steps;
        uint8_t *p = buf;
        uint8_t *ecc_calc = chip->buffers->ecccalc;
        uint8_t *ecc_code = chip->buffers->ecccode;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
        unsigned int max_bitflips = 0;
 
        chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
@@ -1293,8 +1334,10 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 
-       for (i = 0; i < chip->ecc.total; i++)
-               ecc_code[i] = chip->oob_poi[eccpos[i]];
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        eccsteps = chip->ecc.steps;
        p = buf;
@@ -1326,14 +1369,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
                        uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
                        int page)
 {
-       int start_step, end_step, num_steps;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
+       int start_step, end_step, num_steps, ret;
        uint8_t *p;
        int data_col_addr, i, gaps = 0;
        int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
        int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
-       int index;
+       int index, section = 0;
        unsigned int max_bitflips = 0;
+       struct mtd_oob_region oobregion = { };
 
        /* Column address within the page aligned to ECC size (256bytes) */
        start_step = data_offs / chip->ecc.size;
@@ -1361,12 +1404,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
         * The performance is faster if we position offsets according to
         * ecc.pos. Let's make sure that there are no gaps in ECC positions.
         */
-       for (i = 0; i < eccfrag_len - 1; i++) {
-               if (eccpos[i + index] + 1 != eccpos[i + index + 1]) {
-                       gaps = 1;
-                       break;
-               }
-       }
+       ret = mtd_ooblayout_find_eccregion(mtd, index, &section, &oobregion);
+       if (ret)
+               return ret;
+
+       if (oobregion.length < eccfrag_len)
+               gaps = 1;
+
        if (gaps) {
                chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
                chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -1375,20 +1419,23 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
                 * Send the command to read the particular ECC bytes take care
                 * about buswidth alignment in read_buf.
                 */
-               aligned_pos = eccpos[index] & ~(busw - 1);
+               aligned_pos = oobregion.offset & ~(busw - 1);
                aligned_len = eccfrag_len;
-               if (eccpos[index] & (busw - 1))
+               if (oobregion.offset & (busw - 1))
                        aligned_len++;
-               if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
+               if ((oobregion.offset + (num_steps * chip->ecc.bytes)) &
+                   (busw - 1))
                        aligned_len++;
 
                chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
-                                       mtd->writesize + aligned_pos, -1);
+                             mtd->writesize + aligned_pos, -1);
                chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
        }
 
-       for (i = 0; i < eccfrag_len; i++)
-               chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
+       ret = mtd_ooblayout_get_eccbytes(mtd, chip->buffers->ecccode,
+                                        chip->oob_poi, index, eccfrag_len);
+       if (ret)
+               return ret;
 
        p = bufpoi + data_col_addr;
        for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
@@ -1429,13 +1476,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
 static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                                uint8_t *buf, int oob_required, int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        int eccbytes = chip->ecc.bytes;
        int eccsteps = chip->ecc.steps;
        uint8_t *p = buf;
        uint8_t *ecc_calc = chip->buffers->ecccalc;
        uint8_t *ecc_code = chip->buffers->ecccode;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
        unsigned int max_bitflips = 0;
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
@@ -1445,8 +1491,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
        }
        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 
-       for (i = 0; i < chip->ecc.total; i++)
-               ecc_code[i] = chip->oob_poi[eccpos[i]];
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        eccsteps = chip->ecc.steps;
        p = buf;
@@ -1491,12 +1539,11 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
 static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
        struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        int eccbytes = chip->ecc.bytes;
        int eccsteps = chip->ecc.steps;
        uint8_t *p = buf;
        uint8_t *ecc_code = chip->buffers->ecccode;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
        uint8_t *ecc_calc = chip->buffers->ecccalc;
        unsigned int max_bitflips = 0;
 
@@ -1505,8 +1552,10 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
        chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
 
-       for (i = 0; i < chip->ecc.total; i++)
-               ecc_code[i] = chip->oob_poi[eccpos[i]];
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
                int stat;
@@ -1607,14 +1656,17 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
 
 /**
  * nand_transfer_oob - [INTERN] Transfer oob to client buffer
- * @chip: nand chip structure
+ * @mtd: mtd info structure
  * @oob: oob destination address
  * @ops: oob ops structure
  * @len: size of oob to transfer
  */
-static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob,
                                  struct mtd_oob_ops *ops, size_t len)
 {
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       int ret;
+
        switch (ops->mode) {
 
        case MTD_OPS_PLACE_OOB:
@@ -1622,31 +1674,12 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
                memcpy(oob, chip->oob_poi + ops->ooboffs, len);
                return oob + len;
 
-       case MTD_OPS_AUTO_OOB: {
-               struct nand_oobfree *free = chip->ecc.layout->oobfree;
-               uint32_t boffs = 0, roffs = ops->ooboffs;
-               size_t bytes = 0;
-
-               for (; free->length && len; free++, len -= bytes) {
-                       /* Read request not from offset 0? */
-                       if (unlikely(roffs)) {
-                               if (roffs >= free->length) {
-                                       roffs -= free->length;
-                                       continue;
-                               }
-                               boffs = free->offset + roffs;
-                               bytes = min_t(size_t, len,
-                                             (free->length - roffs));
-                               roffs = 0;
-                       } else {
-                               bytes = min_t(size_t, len, free->length);
-                               boffs = free->offset;
-                       }
-                       memcpy(oob, chip->oob_poi + boffs, bytes);
-                       oob += bytes;
-               }
-               return oob;
-       }
+       case MTD_OPS_AUTO_OOB:
+               ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi,
+                                                 ops->ooboffs, len);
+               BUG_ON(ret);
+               return oob + len;
+
        default:
                BUG();
        }
@@ -1780,7 +1813,7 @@ read_retry:
                                int toread = min(oobreadlen, max_oobsize);
 
                                if (toread) {
-                                       oob = nand_transfer_oob(chip,
+                                       oob = nand_transfer_oob(mtd,
                                                oob, ops, toread);
                                        oobreadlen -= toread;
                                }
@@ -1893,13 +1926,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
  * @chip: nand chip info structure
  * @page: page number to read
  */
-static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
-                            int page)
+int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
 {
        chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
        return 0;
 }
+EXPORT_SYMBOL(nand_read_oob_std);
 
 /**
  * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
@@ -1908,8 +1941,8 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
  * @chip: nand chip info structure
  * @page: page number to read
  */
-static int nand_read_oob_syndrome(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)
 {
        int length = mtd->oobsize;
        int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
@@ -1937,6 +1970,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
 
        return 0;
 }
+EXPORT_SYMBOL(nand_read_oob_syndrome);
 
 /**
  * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
@@ -1944,8 +1978,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
  * @chip: nand chip info structure
  * @page: page number to write
  */
-static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
-                             int page)
+int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
 {
        int status = 0;
        const uint8_t *buf = chip->oob_poi;
@@ -1960,6 +1993,7 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
 
        return status & NAND_STATUS_FAIL ? -EIO : 0;
 }
+EXPORT_SYMBOL(nand_write_oob_std);
 
 /**
  * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
@@ -1968,8 +2002,8 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
  * @chip: nand chip info structure
  * @page: page number to write
  */
-static int nand_write_oob_syndrome(struct mtd_info *mtd,
-                                  struct nand_chip *chip, int page)
+int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+                           int page)
 {
        int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
        int eccsize = chip->ecc.size, length = mtd->oobsize;
@@ -2019,6 +2053,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
 
        return status & NAND_STATUS_FAIL ? -EIO : 0;
 }
+EXPORT_SYMBOL(nand_write_oob_syndrome);
 
 /**
  * nand_do_read_oob - [INTERN] NAND read out-of-band
@@ -2078,7 +2113,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
                        break;
 
                len = min(len, readlen);
-               buf = nand_transfer_oob(chip, buf, ops, len);
+               buf = nand_transfer_oob(mtd, buf, ops, len);
 
                if (chip->options & NAND_NEED_READRDY) {
                        /* Apply delay or wait for ready/busy pin */
@@ -2237,19 +2272,20 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
                                 const uint8_t *buf, int oob_required,
                                 int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        int eccbytes = chip->ecc.bytes;
        int eccsteps = chip->ecc.steps;
        uint8_t *ecc_calc = chip->buffers->ecccalc;
        const uint8_t *p = buf;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
 
        /* Software ECC calculation */
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 
-       for (i = 0; i < chip->ecc.total; i++)
-               chip->oob_poi[eccpos[i]] = ecc_calc[i];
+       ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
 }
@@ -2266,12 +2302,11 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                                  const uint8_t *buf, int oob_required,
                                  int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        int eccbytes = chip->ecc.bytes;
        int eccsteps = chip->ecc.steps;
        uint8_t *ecc_calc = chip->buffers->ecccalc;
        const uint8_t *p = buf;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
                chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
@@ -2279,8 +2314,10 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
        }
 
-       for (i = 0; i < chip->ecc.total; i++)
-               chip->oob_poi[eccpos[i]] = ecc_calc[i];
+       ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
 
@@ -2308,11 +2345,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
        int ecc_size      = chip->ecc.size;
        int ecc_bytes     = chip->ecc.bytes;
        int ecc_steps     = chip->ecc.steps;
-       uint32_t *eccpos  = chip->ecc.layout->eccpos;
        uint32_t start_step = offset / ecc_size;
        uint32_t end_step   = (offset + data_len - 1) / ecc_size;
        int oob_bytes       = mtd->oobsize / ecc_steps;
-       int step, i;
+       int step, ret;
 
        for (step = 0; step < ecc_steps; step++) {
                /* configure controller for WRITE access */
@@ -2340,8 +2376,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
        /* copy calculated ECC for whole page to chip->buffer->oob */
        /* this include masked-value(0xFF) for unwritten subpages */
        ecc_calc = chip->buffers->ecccalc;
-       for (i = 0; i < chip->ecc.total; i++)
-               chip->oob_poi[eccpos[i]] = ecc_calc[i];
+       ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        /* write OOB buffer to NAND device */
        chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -2478,6 +2516,7 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
                              struct mtd_oob_ops *ops)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
+       int ret;
 
        /*
         * Initialise to all 0xFF, to avoid the possibility of left over OOB
@@ -2492,31 +2531,12 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
                memcpy(chip->oob_poi + ops->ooboffs, oob, len);
                return oob + len;
 
-       case MTD_OPS_AUTO_OOB: {
-               struct nand_oobfree *free = chip->ecc.layout->oobfree;
-               uint32_t boffs = 0, woffs = ops->ooboffs;
-               size_t bytes = 0;
-
-               for (; free->length && len; free++, len -= bytes) {
-                       /* Write request not from offset 0? */
-                       if (unlikely(woffs)) {
-                               if (woffs >= free->length) {
-                                       woffs -= free->length;
-                                       continue;
-                               }
-                               boffs = free->offset + woffs;
-                               bytes = min_t(size_t, len,
-                                             (free->length - woffs));
-                               woffs = 0;
-                       } else {
-                               bytes = min_t(size_t, len, free->length);
-                               boffs = free->offset;
-                       }
-                       memcpy(chip->oob_poi + boffs, oob, bytes);
-                       oob += bytes;
-               }
-               return oob;
-       }
+       case MTD_OPS_AUTO_OOB:
+               ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
+                                                 ops->ooboffs, len);
+               BUG_ON(ret);
+               return oob + len;
+
        default:
                BUG();
        }
@@ -3951,10 +3971,115 @@ ident_done:
        return type;
 }
 
+static const char * const nand_ecc_modes[] = {
+       [NAND_ECC_NONE]         = "none",
+       [NAND_ECC_SOFT]         = "soft",
+       [NAND_ECC_HW]           = "hw",
+       [NAND_ECC_HW_SYNDROME]  = "hw_syndrome",
+       [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
+};
+
+static int of_get_nand_ecc_mode(struct device_node *np)
+{
+       const char *pm;
+       int err, i;
+
+       err = of_property_read_string(np, "nand-ecc-mode", &pm);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
+               if (!strcasecmp(pm, nand_ecc_modes[i]))
+                       return i;
+
+       /*
+        * For backward compatibility we support few obsoleted values that don't
+        * have their mappings into nand_ecc_modes_t anymore (they were merged
+        * with other enums).
+        */
+       if (!strcasecmp(pm, "soft_bch"))
+               return NAND_ECC_SOFT;
+
+       return -ENODEV;
+}
+
+static const char * const nand_ecc_algos[] = {
+       [NAND_ECC_HAMMING]      = "hamming",
+       [NAND_ECC_BCH]          = "bch",
+};
+
+static int of_get_nand_ecc_algo(struct device_node *np)
+{
+       const char *pm;
+       int err, i;
+
+       err = of_property_read_string(np, "nand-ecc-algo", &pm);
+       if (!err) {
+               for (i = NAND_ECC_HAMMING; i < ARRAY_SIZE(nand_ecc_algos); i++)
+                       if (!strcasecmp(pm, nand_ecc_algos[i]))
+                               return i;
+               return -ENODEV;
+       }
+
+       /*
+        * For backward compatibility we also read "nand-ecc-mode" checking
+        * for some obsoleted values that were specifying ECC algorithm.
+        */
+       err = of_property_read_string(np, "nand-ecc-mode", &pm);
+       if (err < 0)
+               return err;
+
+       if (!strcasecmp(pm, "soft"))
+               return NAND_ECC_HAMMING;
+       else if (!strcasecmp(pm, "soft_bch"))
+               return NAND_ECC_BCH;
+
+       return -ENODEV;
+}
+
+static int of_get_nand_ecc_step_size(struct device_node *np)
+{
+       int ret;
+       u32 val;
+
+       ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
+       return ret ? ret : val;
+}
+
+static int of_get_nand_ecc_strength(struct device_node *np)
+{
+       int ret;
+       u32 val;
+
+       ret = of_property_read_u32(np, "nand-ecc-strength", &val);
+       return ret ? ret : val;
+}
+
+static int of_get_nand_bus_width(struct device_node *np)
+{
+       u32 val;
+
+       if (of_property_read_u32(np, "nand-bus-width", &val))
+               return 8;
+
+       switch (val) {
+       case 8:
+       case 16:
+               return val;
+       default:
+               return -EIO;
+       }
+}
+
+static bool of_get_nand_on_flash_bbt(struct device_node *np)
+{
+       return of_property_read_bool(np, "nand-on-flash-bbt");
+}
+
 static int nand_dt_init(struct nand_chip *chip)
 {
        struct device_node *dn = nand_get_flash_node(chip);
-       int ecc_mode, ecc_strength, ecc_step;
+       int ecc_mode, ecc_algo, ecc_strength, ecc_step;
 
        if (!dn)
                return 0;
@@ -3966,6 +4091,7 @@ static int nand_dt_init(struct nand_chip *chip)
                chip->bbt_options |= NAND_BBT_USE_FLASH;
 
        ecc_mode = of_get_nand_ecc_mode(dn);
+       ecc_algo = of_get_nand_ecc_algo(dn);
        ecc_strength = of_get_nand_ecc_strength(dn);
        ecc_step = of_get_nand_ecc_step_size(dn);
 
@@ -3978,6 +4104,9 @@ static int nand_dt_init(struct nand_chip *chip)
        if (ecc_mode >= 0)
                chip->ecc.mode = ecc_mode;
 
+       if (ecc_algo >= 0)
+               chip->ecc.algo = ecc_algo;
+
        if (ecc_strength >= 0)
                chip->ecc.strength = ecc_strength;
 
@@ -4054,6 +4183,82 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 }
 EXPORT_SYMBOL(nand_scan_ident);
 
+static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+       if (WARN_ON(ecc->mode != NAND_ECC_SOFT))
+               return -EINVAL;
+
+       switch (ecc->algo) {
+       case NAND_ECC_HAMMING:
+               ecc->calculate = nand_calculate_ecc;
+               ecc->correct = nand_correct_data;
+               ecc->read_page = nand_read_page_swecc;
+               ecc->read_subpage = nand_read_subpage;
+               ecc->write_page = nand_write_page_swecc;
+               ecc->read_page_raw = nand_read_page_raw;
+               ecc->write_page_raw = nand_write_page_raw;
+               ecc->read_oob = nand_read_oob_std;
+               ecc->write_oob = nand_write_oob_std;
+               if (!ecc->size)
+                       ecc->size = 256;
+               ecc->bytes = 3;
+               ecc->strength = 1;
+               return 0;
+       case NAND_ECC_BCH:
+               if (!mtd_nand_has_bch()) {
+                       WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
+                       return -EINVAL;
+               }
+               ecc->calculate = nand_bch_calculate_ecc;
+               ecc->correct = nand_bch_correct_data;
+               ecc->read_page = nand_read_page_swecc;
+               ecc->read_subpage = nand_read_subpage;
+               ecc->write_page = nand_write_page_swecc;
+               ecc->read_page_raw = nand_read_page_raw;
+               ecc->write_page_raw = nand_write_page_raw;
+               ecc->read_oob = nand_read_oob_std;
+               ecc->write_oob = nand_write_oob_std;
+               /*
+               * Board driver should supply ecc.size and ecc.strength
+               * values to select how many bits are correctable.
+               * Otherwise, default to 4 bits for large page devices.
+               */
+               if (!ecc->size && (mtd->oobsize >= 64)) {
+                       ecc->size = 512;
+                       ecc->strength = 4;
+               }
+
+               /*
+                * if no ecc placement scheme was provided pickup the default
+                * large page one.
+                */
+               if (!mtd->ooblayout) {
+                       /* handle large page devices only */
+                       if (mtd->oobsize < 64) {
+                               WARN(1, "OOB layout is required when using software BCH on small pages\n");
+                               return -EINVAL;
+                       }
+
+                       mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+               }
+
+               /* See nand_bch_init() for details. */
+               ecc->bytes = 0;
+               ecc->priv = nand_bch_init(mtd);
+               if (!ecc->priv) {
+                       WARN(1, "BCH ECC initialization failed!\n");
+                       return -EINVAL;
+               }
+               return 0;
+       default:
+               WARN(1, "Unsupported ECC algorithm!\n");
+               return -EINVAL;
+       }
+}
+
 /*
  * Check if the chip configuration meet the datasheet requirements.
 
@@ -4098,14 +4303,15 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
  */
 int nand_scan_tail(struct mtd_info *mtd)
 {
-       int i;
        struct nand_chip *chip = mtd_to_nand(mtd);
        struct nand_ecc_ctrl *ecc = &chip->ecc;
        struct nand_buffers *nbuf;
+       int ret;
 
        /* New bad blocks should be marked in OOB, flash-based BBT, or both */
-       BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
-                       !(chip->bbt_options & NAND_BBT_USE_FLASH));
+       if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+                  !(chip->bbt_options & NAND_BBT_USE_FLASH)))
+               return -EINVAL;
 
        if (!(chip->options & NAND_OWN_BUFFERS)) {
                nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
@@ -4128,24 +4334,22 @@ int nand_scan_tail(struct mtd_info *mtd)
        /*
         * If no default placement scheme is given, select an appropriate one.
         */
-       if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) {
+       if (!mtd->ooblayout &&
+           !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) {
                switch (mtd->oobsize) {
                case 8:
-                       ecc->layout = &nand_oob_8;
-                       break;
                case 16:
-                       ecc->layout = &nand_oob_16;
+                       mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
                        break;
                case 64:
-                       ecc->layout = &nand_oob_64;
-                       break;
                case 128:
-                       ecc->layout = &nand_oob_128;
+                       mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
                        break;
                default:
-                       pr_warn("No oob scheme defined for oobsize %d\n",
-                                  mtd->oobsize);
-                       BUG();
+                       WARN(1, "No oob scheme defined for oobsize %d\n",
+                               mtd->oobsize);
+                       ret = -EINVAL;
+                       goto err_free;
                }
        }
 
@@ -4161,8 +4365,9 @@ int nand_scan_tail(struct mtd_info *mtd)
        case NAND_ECC_HW_OOB_FIRST:
                /* Similar to NAND_ECC_HW, but a separate read_page handle */
                if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
-                       pr_warn("No ECC functions supplied; hardware ECC not possible\n");
-                       BUG();
+                       WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
+                       ret = -EINVAL;
+                       goto err_free;
                }
                if (!ecc->read_page)
                        ecc->read_page = nand_read_page_hwecc_oob_first;
@@ -4192,8 +4397,9 @@ int nand_scan_tail(struct mtd_info *mtd)
                     ecc->read_page == nand_read_page_hwecc ||
                     !ecc->write_page ||
                     ecc->write_page == nand_write_page_hwecc)) {
-                       pr_warn("No ECC functions supplied; hardware ECC not possible\n");
-                       BUG();
+                       WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
+                       ret = -EINVAL;
+                       goto err_free;
                }
                /* Use standard syndrome read/write page function? */
                if (!ecc->read_page)
@@ -4211,61 +4417,22 @@ int nand_scan_tail(struct mtd_info *mtd)
 
                if (mtd->writesize >= ecc->size) {
                        if (!ecc->strength) {
-                               pr_warn("Driver must set ecc.strength when using hardware ECC\n");
-                               BUG();
+                               WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
+                               ret = -EINVAL;
+                               goto err_free;
                        }
                        break;
                }
                pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
                        ecc->size, mtd->writesize);
                ecc->mode = NAND_ECC_SOFT;
+               ecc->algo = NAND_ECC_HAMMING;
 
        case NAND_ECC_SOFT:
-               ecc->calculate = nand_calculate_ecc;
-               ecc->correct = nand_correct_data;
-               ecc->read_page = nand_read_page_swecc;
-               ecc->read_subpage = nand_read_subpage;
-               ecc->write_page = nand_write_page_swecc;
-               ecc->read_page_raw = nand_read_page_raw;
-               ecc->write_page_raw = nand_write_page_raw;
-               ecc->read_oob = nand_read_oob_std;
-               ecc->write_oob = nand_write_oob_std;
-               if (!ecc->size)
-                       ecc->size = 256;
-               ecc->bytes = 3;
-               ecc->strength = 1;
-               break;
-
-       case NAND_ECC_SOFT_BCH:
-               if (!mtd_nand_has_bch()) {
-                       pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
-                       BUG();
-               }
-               ecc->calculate = nand_bch_calculate_ecc;
-               ecc->correct = nand_bch_correct_data;
-               ecc->read_page = nand_read_page_swecc;
-               ecc->read_subpage = nand_read_subpage;
-               ecc->write_page = nand_write_page_swecc;
-               ecc->read_page_raw = nand_read_page_raw;
-               ecc->write_page_raw = nand_write_page_raw;
-               ecc->read_oob = nand_read_oob_std;
-               ecc->write_oob = nand_write_oob_std;
-               /*
-                * Board driver should supply ecc.size and ecc.strength values
-                * to select how many bits are correctable. Otherwise, default
-                * to 4 bits for large page devices.
-                */
-               if (!ecc->size && (mtd->oobsize >= 64)) {
-                       ecc->size = 512;
-                       ecc->strength = 4;
-               }
-
-               /* See nand_bch_init() for details. */
-               ecc->bytes = 0;
-               ecc->priv = nand_bch_init(mtd);
-               if (!ecc->priv) {
-                       pr_warn("BCH ECC initialization failed!\n");
-                       BUG();
+               ret = nand_set_ecc_soft_ops(mtd);
+               if (ret) {
+                       ret = -EINVAL;
+                       goto err_free;
                }
                break;
 
@@ -4283,8 +4450,9 @@ int nand_scan_tail(struct mtd_info *mtd)
                break;
 
        default:
-               pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode);
-               BUG();
+               WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode);
+               ret = -EINVAL;
+               goto err_free;
        }
 
        /* For many systems, the standard OOB write also works for raw */
@@ -4293,20 +4461,9 @@ int nand_scan_tail(struct mtd_info *mtd)
        if (!ecc->write_oob_raw)
                ecc->write_oob_raw = ecc->write_oob;
 
-       /*
-        * The number of bytes available for a client to place data into
-        * the out of band area.
-        */
-       mtd->oobavail = 0;
-       if (ecc->layout) {
-               for (i = 0; ecc->layout->oobfree[i].length; i++)
-                       mtd->oobavail += ecc->layout->oobfree[i].length;
-       }
-
-       /* ECC sanity check: warn if it's too weak */
-       if (!nand_ecc_strength_good(mtd))
-               pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
-                       mtd->name);
+       /* propagate ecc info to mtd_info */
+       mtd->ecc_strength = ecc->strength;
+       mtd->ecc_step_size = ecc->size;
 
        /*
         * Set the number of read / write steps for one page depending on ECC
@@ -4314,11 +4471,27 @@ int nand_scan_tail(struct mtd_info *mtd)
         */
        ecc->steps = mtd->writesize / ecc->size;
        if (ecc->steps * ecc->size != mtd->writesize) {
-               pr_warn("Invalid ECC parameters\n");
-               BUG();
+               WARN(1, "Invalid ECC parameters\n");
+               ret = -EINVAL;
+               goto err_free;
        }
        ecc->total = ecc->steps * ecc->bytes;
 
+       /*
+        * The number of bytes available for a client to place data into
+        * the out of band area.
+        */
+       ret = mtd_ooblayout_count_freebytes(mtd);
+       if (ret < 0)
+               ret = 0;
+
+       mtd->oobavail = ret;
+
+       /* ECC sanity check: warn if it's too weak */
+       if (!nand_ecc_strength_good(mtd))
+               pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
+                       mtd->name);
+
        /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
        if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
                switch (ecc->steps) {
@@ -4343,7 +4516,6 @@ int nand_scan_tail(struct mtd_info *mtd)
        /* Large page NAND with SOFT_ECC should support subpage reads */
        switch (ecc->mode) {
        case NAND_ECC_SOFT:
-       case NAND_ECC_SOFT_BCH:
                if (chip->page_shift > 9)
                        chip->options |= NAND_SUBPAGE_READ;
                break;
@@ -4375,10 +4547,6 @@ int nand_scan_tail(struct mtd_info *mtd)
        mtd->_block_markbad = nand_block_markbad;
        mtd->writebufsize = mtd->writesize;
 
-       /* propagate ecc info to mtd_info */
-       mtd->ecclayout = ecc->layout;
-       mtd->ecc_strength = ecc->strength;
-       mtd->ecc_step_size = ecc->size;
        /*
         * Initialize bitflip_threshold to its default prior scan_bbt() call.
         * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
@@ -4393,6 +4561,10 @@ int nand_scan_tail(struct mtd_info *mtd)
 
        /* Build bad block table */
        return chip->scan_bbt(mtd);
+err_free:
+       if (!(chip->options & NAND_OWN_BUFFERS))
+               kfree(chip->buffers);
+       return ret;
 }
 EXPORT_SYMBOL(nand_scan_tail);
 
@@ -4436,7 +4608,8 @@ void nand_release(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
 
-       if (chip->ecc.mode == NAND_ECC_SOFT_BCH)
+       if (chip->ecc.mode == NAND_ECC_SOFT &&
+           chip->ecc.algo == NAND_ECC_BCH)
                nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
 
        mtd_device_unregister(mtd);
index b585bae..44763f8 100644 (file)
 /**
  * struct nand_bch_control - private NAND BCH control structure
  * @bch:       BCH control structure
- * @ecclayout: private ecc layout for this BCH configuration
  * @errloc:    error location array
  * @eccmask:   XOR ecc mask, allows erased pages to be decoded as valid
  */
 struct nand_bch_control {
        struct bch_control   *bch;
-       struct nand_ecclayout ecclayout;
        unsigned int         *errloc;
        unsigned char        *eccmask;
 };
@@ -124,7 +122,6 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
 {
        struct nand_chip *nand = mtd_to_nand(mtd);
        unsigned int m, t, eccsteps, i;
-       struct nand_ecclayout *layout = nand->ecc.layout;
        struct nand_bch_control *nbc = NULL;
        unsigned char *erased_page;
        unsigned int eccsize = nand->ecc.size;
@@ -161,34 +158,10 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
 
        eccsteps = mtd->writesize/eccsize;
 
-       /* if no ecc placement scheme was provided, build one */
-       if (!layout) {
-
-               /* handle large page devices only */
-               if (mtd->oobsize < 64) {
-                       printk(KERN_WARNING "must provide an oob scheme for "
-                              "oobsize %d\n", mtd->oobsize);
-                       goto fail;
-               }
-
-               layout = &nbc->ecclayout;
-               layout->eccbytes = eccsteps*eccbytes;
-
-               /* reserve 2 bytes for bad block marker */
-               if (layout->eccbytes+2 > mtd->oobsize) {
-                       printk(KERN_WARNING "no suitable oob scheme available "
-                              "for oobsize %d eccbytes %u\n", mtd->oobsize,
-                              eccbytes);
-                       goto fail;
-               }
-               /* put ecc bytes at oob tail */
-               for (i = 0; i < layout->eccbytes; i++)
-                       layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i;
-
-               layout->oobfree[0].offset = 2;
-               layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
-
-               nand->ecc.layout = layout;
+       /* Check that we have an oob layout description. */
+       if (!mtd->ooblayout) {
+               pr_warn("missing oob scheme");
+               goto fail;
        }
 
        /* sanity checks */
@@ -196,7 +169,18 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
                printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
                goto fail;
        }
-       if (layout->eccbytes != (eccsteps*eccbytes)) {
+
+       /*
+        * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(),
+        * which is called by mtd_ooblayout_count_eccbytes().
+        * Make sure they are properly initialized before calling
+        * mtd_ooblayout_count_eccbytes().
+        * FIXME: we should probably rework the sequencing in nand_scan_tail()
+        * to avoid setting those fields twice.
+        */
+       nand->ecc.steps = eccsteps;
+       nand->ecc.total = eccsteps * eccbytes;
+       if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
                printk(KERN_WARNING "invalid ecc layout\n");
                goto fail;
        }
index a58169a..1eb9344 100644 (file)
@@ -569,7 +569,7 @@ static void nandsim_debugfs_remove(struct nandsim *ns)
  *
  * RETURNS: 0 if success, -ENOMEM if memory alloc fails.
  */
-static int alloc_device(struct nandsim *ns)
+static int __init alloc_device(struct nandsim *ns)
 {
        struct file *cfile;
        int i, err;
@@ -654,7 +654,7 @@ static void free_device(struct nandsim *ns)
        }
 }
 
-static char *get_partition_name(int i)
+static char __init *get_partition_name(int i)
 {
        return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i);
 }
@@ -664,7 +664,7 @@ static char *get_partition_name(int i)
  *
  * RETURNS: 0 if success, -ERRNO if failure.
  */
-static int init_nandsim(struct mtd_info *mtd)
+static int __init init_nandsim(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
        struct nandsim   *ns   = nand_get_controller_data(chip);
@@ -2261,6 +2261,7 @@ static int __init ns_init_module(void)
        chip->read_buf   = ns_nand_read_buf;
        chip->read_word  = ns_nand_read_word;
        chip->ecc.mode   = NAND_ECC_SOFT;
+       chip->ecc.algo   = NAND_ECC_HAMMING;
        /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
        /* and 'badblocks' parameters to work */
        chip->options   |= NAND_SKIP_BBTSCAN;
@@ -2338,7 +2339,8 @@ static int __init ns_init_module(void)
                        retval = -EINVAL;
                        goto error;
                }
-               chip->ecc.mode = NAND_ECC_SOFT_BCH;
+               chip->ecc.mode = NAND_ECC_SOFT;
+               chip->ecc.algo = NAND_ECC_BCH;
                chip->ecc.size = 512;
                chip->ecc.strength = bch;
                chip->ecc.bytes = eccbytes;
index dbc5b57..8f64011 100644 (file)
@@ -261,6 +261,7 @@ static int nuc900_nand_probe(struct platform_device *pdev)
        chip->chip_delay        = 50;
        chip->options           = 0;
        chip->ecc.mode          = NAND_ECC_SOFT;
+       chip->ecc.algo          = NAND_ECC_HAMMING;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res);
index 0749ca1..08e1588 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/jiffies.h>
@@ -28,6 +29,7 @@
 #include <linux/mtd/nand_bch.h>
 #include <linux/platform_data/elm.h>
 
+#include <linux/omap-gpmc.h>
 #include <linux/platform_data/mtd-nand-omap2.h>
 
 #define        DRIVER_NAME     "omap2-nand"
@@ -151,13 +153,17 @@ static struct nand_hw_control omap_gpmc_controller = {
 };
 
 struct omap_nand_info {
-       struct omap_nand_platform_data  *pdata;
        struct nand_chip                nand;
        struct platform_device          *pdev;
 
        int                             gpmc_cs;
-       unsigned long                   phys_base;
+       bool                            dev_ready;
+       enum nand_io                    xfer_type;
+       int                             devsize;
        enum omap_ecc                   ecc_opt;
+       struct device_node              *elm_of_node;
+
+       unsigned long                   phys_base;
        struct completion               comp;
        struct dma_chan                 *dma;
        int                             gpmc_irq_fifo;
@@ -168,12 +174,14 @@ struct omap_nand_info {
        } iomode;
        u_char                          *buf;
        int                                     buf_len;
+       /* Interface to GPMC */
        struct gpmc_nand_regs           reg;
-       /* generated at runtime depending on ECC algorithm and layout selected */
-       struct nand_ecclayout           oobinfo;
+       struct gpmc_nand_ops            *ops;
+       bool                            flash_bbt;
        /* fields specific for BCHx_HW ECC scheme */
        struct device                   *elm_dev;
-       struct device_node              *of_node;
+       /* NAND ready gpio */
+       struct gpio_desc                *ready_gpiod;
 };
 
 static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
@@ -208,7 +216,7 @@ static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode,
         */
        val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) |
                PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH |
-               (dma_mode << DMA_MPU_MODE_SHIFT) | (0x1 & is_write));
+               (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1));
        writel(val, info->reg.gpmc_prefetch_config1);
 
        /*  Start the prefetch engine */
@@ -288,14 +296,13 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
 {
        struct omap_nand_info *info = mtd_to_omap(mtd);
        u_char *p = (u_char *)buf;
-       u32     status = 0;
+       bool status;
 
        while (len--) {
                iowrite8(*p++, info->nand.IO_ADDR_W);
                /* wait until buffer is available for write */
                do {
-                       status = readl(info->reg.gpmc_status) &
-                                       STATUS_BUFF_EMPTY;
+                       status = info->ops->nand_writebuffer_empty();
                } while (!status);
        }
 }
@@ -323,7 +330,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
 {
        struct omap_nand_info *info = mtd_to_omap(mtd);
        u16 *p = (u16 *) buf;
-       u32     status = 0;
+       bool status;
        /* FIXME try bursts of writesw() or DMA ... */
        len >>= 1;
 
@@ -331,8 +338,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
                iowrite16(*p++, info->nand.IO_ADDR_W);
                /* wait until buffer is available for write */
                do {
-                       status = readl(info->reg.gpmc_status) &
-                                       STATUS_BUFF_EMPTY;
+                       status = info->ops->nand_writebuffer_empty();
                } while (!status);
        }
 }
@@ -467,17 +473,8 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
        int ret;
        u32 val;
 
-       if (addr >= high_memory) {
-               struct page *p1;
-
-               if (((size_t)addr & PAGE_MASK) !=
-                       ((size_t)(addr + len - 1) & PAGE_MASK))
-                       goto out_copy;
-               p1 = vmalloc_to_page(addr);
-               if (!p1)
-                       goto out_copy;
-               addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK);
-       }
+       if (!virt_addr_valid(addr))
+               goto out_copy;
 
        sg_init_one(&sg, addr, len);
        n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
@@ -497,6 +494,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
        tx->callback_param = &info->comp;
        dmaengine_submit(tx);
 
+       init_completion(&info->comp);
+
+       /* setup and start DMA using dma_addr */
+       dma_async_issue_pending(info->dma);
+
        /*  configure and start prefetch transfer */
        ret = omap_prefetch_enable(info->gpmc_cs,
                PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info);
@@ -504,10 +506,6 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
                /* PFPW engine is busy, use cpu copy method */
                goto out_copy_unmap;
 
-       init_completion(&info->comp);
-       dma_async_issue_pending(info->dma);
-
-       /* setup and start DMA using dma_addr */
        wait_for_completion(&info->comp);
        tim = 0;
        limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
@@ -1017,21 +1015,16 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
 }
 
 /**
- * omap_dev_ready - calls the platform specific dev_ready function
+ * omap_dev_ready - checks the NAND Ready GPIO line
  * @mtd: MTD device structure
+ *
+ * Returns true if ready and false if busy.
  */
 static int omap_dev_ready(struct mtd_info *mtd)
 {
-       unsigned int val = 0;
        struct omap_nand_info *info = mtd_to_omap(mtd);
 
-       val = readl(info->reg.gpmc_status);
-
-       if ((val & 0x100) == 0x100) {
-               return 1;
-       } else {
-               return 0;
-       }
+       return gpiod_get_value(info->ready_gpiod);
 }
 
 /**
@@ -1495,9 +1488,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
 static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
                               const uint8_t *buf, int oob_required, int page)
 {
-       int i;
+       int ret;
        uint8_t *ecc_calc = chip->buffers->ecccalc;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
 
        /* Enable GPMC ecc engine */
        chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
@@ -1508,8 +1500,10 @@ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
        /* Update ecc vector from GPMC result registers */
        chip->ecc.calculate(mtd, buf, &ecc_calc[0]);
 
-       for (i = 0; i < chip->ecc.total; i++)
-               chip->oob_poi[eccpos[i]] = ecc_calc[i];
+       ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        /* Write ecc vector to OOB area */
        chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -1536,10 +1530,7 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
 {
        uint8_t *ecc_calc = chip->buffers->ecccalc;
        uint8_t *ecc_code = chip->buffers->ecccode;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
-       uint8_t *oob = &chip->oob_poi[eccpos[0]];
-       uint32_t oob_pos = mtd->writesize + chip->ecc.layout->eccpos[0];
-       int stat;
+       int stat, ret;
        unsigned int max_bitflips = 0;
 
        /* Enable GPMC ecc engine */
@@ -1549,13 +1540,18 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
        chip->read_buf(mtd, buf, mtd->writesize);
 
        /* Read oob bytes */
-       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1);
-       chip->read_buf(mtd, oob, chip->ecc.total);
+       chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+                     mtd->writesize + BADBLOCK_MARKER_LENGTH, -1);
+       chip->read_buf(mtd, chip->oob_poi + BADBLOCK_MARKER_LENGTH,
+                      chip->ecc.total);
 
        /* Calculate ecc bytes */
        chip->ecc.calculate(mtd, buf, ecc_calc);
 
-       memcpy(ecc_code, &chip->oob_poi[eccpos[0]], chip->ecc.total);
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc);
 
@@ -1630,7 +1626,7 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
                        "CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
                return false;
        }
-       if (ecc_needs_elm && !is_elm_present(info, pdata->elm_of_node)) {
+       if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) {
                dev_err(&info->pdev->dev, "ELM not available\n");
                return false;
        }
@@ -1638,43 +1634,227 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
        return true;
 }
 
+static const char * const nand_xfer_types[] = {
+       [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
+       [NAND_OMAP_POLLED] = "polled",
+       [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
+       [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
+};
+
+static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
+{
+       struct device_node *child = dev->of_node;
+       int i;
+       const char *s;
+       u32 cs;
+
+       if (of_property_read_u32(child, "reg", &cs) < 0) {
+               dev_err(dev, "reg not found in DT\n");
+               return -EINVAL;
+       }
+
+       info->gpmc_cs = cs;
+
+       /* detect availability of ELM module. Won't be present pre-OMAP4 */
+       info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
+       if (!info->elm_of_node)
+               dev_dbg(dev, "ti,elm-id not in DT\n");
+
+       /* select ecc-scheme for NAND */
+       if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
+               dev_err(dev, "ti,nand-ecc-opt not found\n");
+               return -EINVAL;
+       }
+
+       if (!strcmp(s, "sw")) {
+               info->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
+       } else if (!strcmp(s, "ham1") ||
+                  !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) {
+               info->ecc_opt = OMAP_ECC_HAM1_CODE_HW;
+       } else if (!strcmp(s, "bch4")) {
+               if (info->elm_of_node)
+                       info->ecc_opt = OMAP_ECC_BCH4_CODE_HW;
+               else
+                       info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
+       } else if (!strcmp(s, "bch8")) {
+               if (info->elm_of_node)
+                       info->ecc_opt = OMAP_ECC_BCH8_CODE_HW;
+               else
+                       info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
+       } else if (!strcmp(s, "bch16")) {
+               info->ecc_opt = OMAP_ECC_BCH16_CODE_HW;
+       } else {
+               dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n");
+               return -EINVAL;
+       }
+
+       /* select data transfer mode */
+       if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) {
+               for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) {
+                       if (!strcasecmp(s, nand_xfer_types[i])) {
+                               info->xfer_type = i;
+                               return 0;
+                       }
+               }
+
+               dev_err(dev, "unrecognized value for ti,nand-xfer-type\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
+                             struct mtd_oob_region *oobregion)
+{
+       struct omap_nand_info *info = mtd_to_omap(mtd);
+       struct nand_chip *chip = &info->nand;
+       int off = BADBLOCK_MARKER_LENGTH;
+
+       if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
+           !(chip->options & NAND_BUSWIDTH_16))
+               off = 1;
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = off;
+       oobregion->length = chip->ecc.total;
+
+       return 0;
+}
+
+static int omap_ooblayout_free(struct mtd_info *mtd, int section,
+                              struct mtd_oob_region *oobregion)
+{
+       struct omap_nand_info *info = mtd_to_omap(mtd);
+       struct nand_chip *chip = &info->nand;
+       int off = BADBLOCK_MARKER_LENGTH;
+
+       if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
+           !(chip->options & NAND_BUSWIDTH_16))
+               off = 1;
+
+       if (section)
+               return -ERANGE;
+
+       off += chip->ecc.total;
+       if (off >= mtd->oobsize)
+               return -ERANGE;
+
+       oobregion->offset = off;
+       oobregion->length = mtd->oobsize - off;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops omap_ooblayout_ops = {
+       .ecc = omap_ooblayout_ecc,
+       .free = omap_ooblayout_free,
+};
+
+static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       int off = BADBLOCK_MARKER_LENGTH;
+
+       if (section >= chip->ecc.steps)
+               return -ERANGE;
+
+       /*
+        * When SW correction is employed, one OMAP specific marker byte is
+        * reserved after each ECC step.
+        */
+       oobregion->offset = off + (section * (chip->ecc.bytes + 1));
+       oobregion->length = chip->ecc.bytes;
+
+       return 0;
+}
+
+static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       int off = BADBLOCK_MARKER_LENGTH;
+
+       if (section)
+               return -ERANGE;
+
+       /*
+        * When SW correction is employed, one OMAP specific marker byte is
+        * reserved after each ECC step.
+        */
+       off += ((chip->ecc.bytes + 1) * chip->ecc.steps);
+       if (off >= mtd->oobsize)
+               return -ERANGE;
+
+       oobregion->offset = off;
+       oobregion->length = mtd->oobsize - off;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
+       .ecc = omap_sw_ooblayout_ecc,
+       .free = omap_sw_ooblayout_free,
+};
+
 static int omap_nand_probe(struct platform_device *pdev)
 {
        struct omap_nand_info           *info;
-       struct omap_nand_platform_data  *pdata;
+       struct omap_nand_platform_data  *pdata = NULL;
        struct mtd_info                 *mtd;
        struct nand_chip                *nand_chip;
-       struct nand_ecclayout           *ecclayout;
        int                             err;
-       int                             i;
        dma_cap_mask_t                  mask;
        unsigned                        sig;
-       unsigned                        oob_index;
        struct resource                 *res;
-
-       pdata = dev_get_platdata(&pdev->dev);
-       if (pdata == NULL) {
-               dev_err(&pdev->dev, "platform data missing\n");
-               return -ENODEV;
-       }
+       struct device                   *dev = &pdev->dev;
+       int                             min_oobbytes = BADBLOCK_MARKER_LENGTH;
+       int                             oobbytes_per_step;
 
        info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
                                GFP_KERNEL);
        if (!info)
                return -ENOMEM;
 
+       info->pdev = pdev;
+
+       if (dev->of_node) {
+               if (omap_get_dt_info(dev, info))
+                       return -EINVAL;
+       } else {
+               pdata = dev_get_platdata(&pdev->dev);
+               if (!pdata) {
+                       dev_err(&pdev->dev, "platform data missing\n");
+                       return -EINVAL;
+               }
+
+               info->gpmc_cs = pdata->cs;
+               info->reg = pdata->reg;
+               info->ecc_opt = pdata->ecc_opt;
+               if (pdata->dev_ready)
+                       dev_info(&pdev->dev, "pdata->dev_ready is deprecated\n");
+
+               info->xfer_type = pdata->xfer_type;
+               info->devsize = pdata->devsize;
+               info->elm_of_node = pdata->elm_of_node;
+               info->flash_bbt = pdata->flash_bbt;
+       }
+
        platform_set_drvdata(pdev, info);
+       info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
+       if (!info->ops) {
+               dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
+               return -ENODEV;
+       }
 
-       info->pdev              = pdev;
-       info->gpmc_cs           = pdata->cs;
-       info->reg               = pdata->reg;
-       info->of_node           = pdata->of_node;
-       info->ecc_opt           = pdata->ecc_opt;
        nand_chip               = &info->nand;
        mtd                     = nand_to_mtd(nand_chip);
        mtd->dev.parent         = &pdev->dev;
        nand_chip->ecc.priv     = NULL;
-       nand_set_flash_node(nand_chip, pdata->of_node);
+       nand_set_flash_node(nand_chip, dev->of_node);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
@@ -1688,6 +1868,13 @@ static int omap_nand_probe(struct platform_device *pdev)
        nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
        nand_chip->cmd_ctrl  = omap_hwcontrol;
 
+       info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
+                                                   GPIOD_IN);
+       if (IS_ERR(info->ready_gpiod)) {
+               dev_err(dev, "failed to get ready gpio\n");
+               return PTR_ERR(info->ready_gpiod);
+       }
+
        /*
         * If RDY/BSY line is connected to OMAP then use the omap ready
         * function and the generic nand_wait function which reads the status
@@ -1695,7 +1882,7 @@ static int omap_nand_probe(struct platform_device *pdev)
         * chip delay which is slightly more than tR (AC Timing) of the NAND
         * device and read status register until you get a failure or success
         */
-       if (pdata->dev_ready) {
+       if (info->ready_gpiod) {
                nand_chip->dev_ready = omap_dev_ready;
                nand_chip->chip_delay = 0;
        } else {
@@ -1703,21 +1890,25 @@ static int omap_nand_probe(struct platform_device *pdev)
                nand_chip->chip_delay = 50;
        }
 
-       if (pdata->flash_bbt)
-               nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
-       else
-               nand_chip->options |= NAND_SKIP_BBTSCAN;
+       if (info->flash_bbt)
+               nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
 
        /* scan NAND device connected to chip controller */
-       nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16;
+       nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
        if (nand_scan_ident(mtd, 1, NULL)) {
-               dev_err(&info->pdev->dev, "scan failed, may be bus-width mismatch\n");
+               dev_err(&info->pdev->dev,
+                       "scan failed, may be bus-width mismatch\n");
                err = -ENXIO;
                goto return_error;
        }
 
+       if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
+               nand_chip->bbt_options |= NAND_BBT_NO_OOB;
+       else
+               nand_chip->options |= NAND_SKIP_BBTSCAN;
+
        /* re-populate low-level callbacks based on xfer modes */
-       switch (pdata->xfer_type) {
+       switch (info->xfer_type) {
        case NAND_OMAP_PREFETCH_POLLED:
                nand_chip->read_buf   = omap_read_buf_pref;
                nand_chip->write_buf  = omap_write_buf_pref;
@@ -1797,7 +1988,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 
        default:
                dev_err(&pdev->dev,
-                       "xfer_type(%d) not supported!\n", pdata->xfer_type);
+                       "xfer_type(%d) not supported!\n", info->xfer_type);
                err = -EINVAL;
                goto return_error;
        }
@@ -1809,16 +2000,15 @@ static int omap_nand_probe(struct platform_device *pdev)
 
        /*
         * Bail out earlier to let NAND_ECC_SOFT code create its own
-        * ecclayout instead of using ours.
+        * ooblayout instead of using ours.
         */
        if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
                nand_chip->ecc.mode = NAND_ECC_SOFT;
+               nand_chip->ecc.algo = NAND_ECC_HAMMING;
                goto scan_tail;
        }
 
        /* populate MTD interface based on ECC scheme */
-       ecclayout               = &info->oobinfo;
-       nand_chip->ecc.layout   = ecclayout;
        switch (info->ecc_opt) {
        case OMAP_ECC_HAM1_CODE_HW:
                pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
@@ -1829,19 +2019,12 @@ static int omap_nand_probe(struct platform_device *pdev)
                nand_chip->ecc.calculate        = omap_calculate_ecc;
                nand_chip->ecc.hwctl            = omap_enable_hwecc;
                nand_chip->ecc.correct          = omap_correct_data;
-               /* define ECC layout */
-               ecclayout->eccbytes             = nand_chip->ecc.bytes *
-                                                       (mtd->writesize /
-                                                       nand_chip->ecc.size);
-               if (nand_chip->options & NAND_BUSWIDTH_16)
-                       oob_index               = BADBLOCK_MARKER_LENGTH;
-               else
-                       oob_index               = 1;
-               for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
-                       ecclayout->eccpos[i]    = oob_index;
-               /* no reserved-marker in ecclayout for this ecc-scheme */
-               ecclayout->oobfree->offset      =
-                               ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+               mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+               oobbytes_per_step               = nand_chip->ecc.bytes;
+
+               if (!(nand_chip->options & NAND_BUSWIDTH_16))
+                       min_oobbytes            = 1;
+
                break;
 
        case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
@@ -1853,19 +2036,9 @@ static int omap_nand_probe(struct platform_device *pdev)
                nand_chip->ecc.hwctl            = omap_enable_hwecc_bch;
                nand_chip->ecc.correct          = nand_bch_correct_data;
                nand_chip->ecc.calculate        = omap_calculate_ecc_bch;
-               /* define ECC layout */
-               ecclayout->eccbytes             = nand_chip->ecc.bytes *
-                                                       (mtd->writesize /
-                                                       nand_chip->ecc.size);
-               oob_index                       = BADBLOCK_MARKER_LENGTH;
-               for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
-                       ecclayout->eccpos[i] = oob_index;
-                       if (((i + 1) % nand_chip->ecc.bytes) == 0)
-                               oob_index++;
-               }
-               /* include reserved-marker in ecclayout->oobfree calculation */
-               ecclayout->oobfree->offset      = 1 +
-                               ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+               mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
+               /* Reserve one byte for the OMAP marker */
+               oobbytes_per_step               = nand_chip->ecc.bytes + 1;
                /* software bch library is used for locating errors */
                nand_chip->ecc.priv             = nand_bch_init(mtd);
                if (!nand_chip->ecc.priv) {
@@ -1887,16 +2060,8 @@ static int omap_nand_probe(struct platform_device *pdev)
                nand_chip->ecc.calculate        = omap_calculate_ecc_bch;
                nand_chip->ecc.read_page        = omap_read_page_bch;
                nand_chip->ecc.write_page       = omap_write_page_bch;
-               /* define ECC layout */
-               ecclayout->eccbytes             = nand_chip->ecc.bytes *
-                                                       (mtd->writesize /
-                                                       nand_chip->ecc.size);
-               oob_index                       = BADBLOCK_MARKER_LENGTH;
-               for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
-                       ecclayout->eccpos[i]    = oob_index;
-               /* reserved marker already included in ecclayout->eccbytes */
-               ecclayout->oobfree->offset      =
-                               ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+               mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+               oobbytes_per_step               = nand_chip->ecc.bytes;
 
                err = elm_config(info->elm_dev, BCH4_ECC,
                                 mtd->writesize / nand_chip->ecc.size,
@@ -1914,19 +2079,9 @@ static int omap_nand_probe(struct platform_device *pdev)
                nand_chip->ecc.hwctl            = omap_enable_hwecc_bch;
                nand_chip->ecc.correct          = nand_bch_correct_data;
                nand_chip->ecc.calculate        = omap_calculate_ecc_bch;
-               /* define ECC layout */
-               ecclayout->eccbytes             = nand_chip->ecc.bytes *
-                                                       (mtd->writesize /
-                                                       nand_chip->ecc.size);
-               oob_index                       = BADBLOCK_MARKER_LENGTH;
-               for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
-                       ecclayout->eccpos[i] = oob_index;
-                       if (((i + 1) % nand_chip->ecc.bytes) == 0)
-                               oob_index++;
-               }
-               /* include reserved-marker in ecclayout->oobfree calculation */
-               ecclayout->oobfree->offset      = 1 +
-                               ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
+               mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
+               /* Reserve one byte for the OMAP marker */
+               oobbytes_per_step               = nand_chip->ecc.bytes + 1;
                /* software bch library is used for locating errors */
                nand_chip->ecc.priv             = nand_bch_init(mtd);
                if (!nand_chip->ecc.priv) {
@@ -1948,6 +2103,8 @@ static int omap_nand_probe(struct platform_device *pdev)
                nand_chip->ecc.calculate        = omap_calculate_ecc_bch;
                nand_chip->ecc.read_page        = omap_read_page_bch;
                nand_chip->ecc.write_page       = omap_write_page_bch;
+               mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+               oobbytes_per_step               = nand_chip->ecc.bytes;
 
                err = elm_config(info->elm_dev, BCH8_ECC,
                                 mtd->writesize / nand_chip->ecc.size,
@@ -1955,16 +2112,6 @@ static int omap_nand_probe(struct platform_device *pdev)
                if (err < 0)
                        goto return_error;
 
-               /* define ECC layout */
-               ecclayout->eccbytes             = nand_chip->ecc.bytes *
-                                                       (mtd->writesize /
-                                                       nand_chip->ecc.size);
-               oob_index                       = BADBLOCK_MARKER_LENGTH;
-               for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
-                       ecclayout->eccpos[i]    = oob_index;
-               /* reserved marker already included in ecclayout->eccbytes */
-               ecclayout->oobfree->offset      =
-                               ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
                break;
 
        case OMAP_ECC_BCH16_CODE_HW:
@@ -1978,6 +2125,8 @@ static int omap_nand_probe(struct platform_device *pdev)
                nand_chip->ecc.calculate        = omap_calculate_ecc_bch;
                nand_chip->ecc.read_page        = omap_read_page_bch;
                nand_chip->ecc.write_page       = omap_write_page_bch;
+               mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+               oobbytes_per_step               = nand_chip->ecc.bytes;
 
                err = elm_config(info->elm_dev, BCH16_ECC,
                                 mtd->writesize / nand_chip->ecc.size,
@@ -1985,16 +2134,6 @@ static int omap_nand_probe(struct platform_device *pdev)
                if (err < 0)
                        goto return_error;
 
-               /* define ECC layout */
-               ecclayout->eccbytes             = nand_chip->ecc.bytes *
-                                                       (mtd->writesize /
-                                                       nand_chip->ecc.size);
-               oob_index                       = BADBLOCK_MARKER_LENGTH;
-               for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
-                       ecclayout->eccpos[i]    = oob_index;
-               /* reserved marker already included in ecclayout->eccbytes */
-               ecclayout->oobfree->offset      =
-                               ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
                break;
        default:
                dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
@@ -2002,13 +2141,13 @@ static int omap_nand_probe(struct platform_device *pdev)
                goto return_error;
        }
 
-       /* all OOB bytes from oobfree->offset till end off OOB are free */
-       ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
        /* check if NAND device's OOB is enough to store ECC signatures */
-       if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) {
+       min_oobbytes += (oobbytes_per_step *
+                        (mtd->writesize / nand_chip->ecc.size));
+       if (mtd->oobsize < min_oobbytes) {
                dev_err(&info->pdev->dev,
                        "not enough OOB bytes required = %d, available=%d\n",
-                       ecclayout->eccbytes, mtd->oobsize);
+                       min_oobbytes, mtd->oobsize);
                err = -EINVAL;
                goto return_error;
        }
@@ -2020,7 +2159,10 @@ scan_tail:
                goto return_error;
        }
 
-       mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
+       if (dev->of_node)
+               mtd_device_register(mtd, NULL, 0);
+       else
+               mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
 
        platform_set_drvdata(pdev, mtd);
 
@@ -2051,11 +2193,17 @@ static int omap_nand_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id omap_nand_ids[] = {
+       { .compatible = "ti,omap2-nand", },
+       {},
+};
+
 static struct platform_driver omap_nand_driver = {
        .probe          = omap_nand_probe,
        .remove         = omap_nand_remove,
        .driver         = {
                .name   = DRIVER_NAME,
+               .of_match_table = of_match_ptr(omap_nand_ids),
        },
 };
 
index d4614bf..40a7c4a 100644 (file)
@@ -130,6 +130,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
        nc->cmd_ctrl = orion_nand_cmd_ctrl;
        nc->read_buf = orion_nand_read_buf;
        nc->ecc.mode = NAND_ECC_SOFT;
+       nc->ecc.algo = NAND_ECC_HAMMING;
 
        if (board->chip_delay)
                nc->chip_delay = board->chip_delay;
index 3ab53ca..5de7591 100644 (file)
@@ -92,8 +92,9 @@ int pasemi_device_ready(struct mtd_info *mtd)
 
 static int pasemi_nand_probe(struct platform_device *ofdev)
 {
+       struct device *dev = &ofdev->dev;
        struct pci_dev *pdev;
-       struct device_node *np = ofdev->dev.of_node;
+       struct device_node *np = dev->of_node;
        struct resource res;
        struct nand_chip *chip;
        int err = 0;
@@ -107,13 +108,11 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
        if (pasemi_nand_mtd)
                return -ENODEV;
 
-       pr_debug("pasemi_nand at %pR\n", &res);
+       dev_dbg(dev, "pasemi_nand at %pR\n", &res);
 
        /* Allocate memory for MTD device structure and private data */
        chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
        if (!chip) {
-               printk(KERN_WARNING
-                      "Unable to allocate PASEMI NAND MTD device structure\n");
                err = -ENOMEM;
                goto out;
        }
@@ -121,7 +120,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
        pasemi_nand_mtd = nand_to_mtd(chip);
 
        /* Link the private data with the MTD structure */
-       pasemi_nand_mtd->dev.parent = &ofdev->dev;
+       pasemi_nand_mtd->dev.parent = dev;
 
        chip->IO_ADDR_R = of_iomap(np, 0);
        chip->IO_ADDR_W = chip->IO_ADDR_R;
@@ -151,6 +150,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
        chip->write_buf = pasemi_write_buf;
        chip->chip_delay = 0;
        chip->ecc.mode = NAND_ECC_SOFT;
+       chip->ecc.algo = NAND_ECC_HAMMING;
 
        /* Enable the following for a flash based bad block table */
        chip->bbt_options = NAND_BBT_USE_FLASH;
@@ -162,13 +162,13 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
        }
 
        if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) {
-               printk(KERN_ERR "pasemi_nand: Unable to register MTD device\n");
+               dev_err(dev, "Unable to register MTD device\n");
                err = -ENODEV;
                goto out_lpc;
        }
 
-       printk(KERN_INFO "PA Semi NAND flash at %08llx, control at I/O %x\n",
-              res.start, lpcctl);
+       dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res,
+                lpcctl);
 
        return 0;
 
index e4e50da..415a53a 100644 (file)
@@ -74,6 +74,7 @@ static int plat_nand_probe(struct platform_device *pdev)
 
        data->chip.ecc.hwctl = pdata->ctrl.hwcontrol;
        data->chip.ecc.mode = NAND_ECC_SOFT;
+       data->chip.ecc.algo = NAND_ECC_HAMMING;
 
        platform_set_drvdata(pdev, data);
 
index d650885..436dd6d 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/of_mtd.h>
 #include <linux/platform_data/mtd-nand-pxa3xx.h>
 
 #define        CHIP_DELAY_TIMEOUT      msecs_to_jiffies(200)
@@ -324,6 +323,62 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
        { 0xba20, 16, 16, &timing[3] },
 };
 
+static int pxa3xx_ooblayout_ecc(struct mtd_info *mtd, int section,
+                               struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+       struct pxa3xx_nand_info *info = host->info_data;
+       int nchunks = mtd->writesize / info->chunk_size;
+
+       if (section >= nchunks)
+               return -ERANGE;
+
+       oobregion->offset = ((info->ecc_size + info->spare_size) * section) +
+                           info->spare_size;
+       oobregion->length = info->ecc_size;
+
+       return 0;
+}
+
+static int pxa3xx_ooblayout_free(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+       struct pxa3xx_nand_info *info = host->info_data;
+       int nchunks = mtd->writesize / info->chunk_size;
+
+       if (section >= nchunks)
+               return -ERANGE;
+
+       if (!info->spare_size)
+               return 0;
+
+       oobregion->offset = section * (info->ecc_size + info->spare_size);
+       oobregion->length = info->spare_size;
+       if (!section) {
+               /*
+                * Bootrom looks in bytes 0 & 5 for bad blocks for the
+                * 4KB page / 4bit BCH combination.
+                */
+               if (mtd->writesize == 4096 && info->chunk_size == 2048) {
+                       oobregion->offset += 6;
+                       oobregion->length -= 6;
+               } else {
+                       oobregion->offset += 2;
+                       oobregion->length -= 2;
+               }
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops pxa3xx_ooblayout_ops = {
+       .ecc = pxa3xx_ooblayout_ecc,
+       .free = pxa3xx_ooblayout_free,
+};
+
 static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
 static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' };
 
@@ -347,41 +402,6 @@ static struct nand_bbt_descr bbt_mirror_descr = {
        .pattern = bbt_mirror_pattern
 };
 
-static struct nand_ecclayout ecc_layout_2KB_bch4bit = {
-       .eccbytes = 32,
-       .eccpos = {
-               32, 33, 34, 35, 36, 37, 38, 39,
-               40, 41, 42, 43, 44, 45, 46, 47,
-               48, 49, 50, 51, 52, 53, 54, 55,
-               56, 57, 58, 59, 60, 61, 62, 63},
-       .oobfree = { {2, 30} }
-};
-
-static struct nand_ecclayout ecc_layout_4KB_bch4bit = {
-       .eccbytes = 64,
-       .eccpos = {
-               32,  33,  34,  35,  36,  37,  38,  39,
-               40,  41,  42,  43,  44,  45,  46,  47,
-               48,  49,  50,  51,  52,  53,  54,  55,
-               56,  57,  58,  59,  60,  61,  62,  63,
-               96,  97,  98,  99,  100, 101, 102, 103,
-               104, 105, 106, 107, 108, 109, 110, 111,
-               112, 113, 114, 115, 116, 117, 118, 119,
-               120, 121, 122, 123, 124, 125, 126, 127},
-       /* Bootrom looks in bytes 0 & 5 for bad blocks */
-       .oobfree = { {6, 26}, { 64, 32} }
-};
-
-static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
-       .eccbytes = 128,
-       .eccpos = {
-               32,  33,  34,  35,  36,  37,  38,  39,
-               40,  41,  42,  43,  44,  45,  46,  47,
-               48,  49,  50,  51,  52,  53,  54,  55,
-               56,  57,  58,  59,  60,  61,  62,  63},
-       .oobfree = { }
-};
-
 #define NDTR0_tCH(c)   (min((c), 7) << 19)
 #define NDTR0_tCS(c)   (min((c), 7) << 16)
 #define NDTR0_tWH(c)   (min((c), 7) << 11)
@@ -1546,9 +1566,12 @@ static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
 }
 
 static int pxa_ecc_init(struct pxa3xx_nand_info *info,
-                       struct nand_ecc_ctrl *ecc,
+                       struct mtd_info *mtd,
                        int strength, int ecc_stepsize, int page_size)
 {
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+
        if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
                info->nfullchunks = 1;
                info->ntotalchunks = 1;
@@ -1582,7 +1605,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
                info->ecc_size = 32;
                ecc->mode = NAND_ECC_HW;
                ecc->size = info->chunk_size;
-               ecc->layout = &ecc_layout_2KB_bch4bit;
+               mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops);
                ecc->strength = 16;
 
        } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
@@ -1594,7 +1617,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
                info->ecc_size = 32;
                ecc->mode = NAND_ECC_HW;
                ecc->size = info->chunk_size;
-               ecc->layout = &ecc_layout_4KB_bch4bit;
+               mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops);
                ecc->strength = 16;
 
        /*
@@ -1612,7 +1635,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
                info->ecc_size = 32;
                ecc->mode = NAND_ECC_HW;
                ecc->size = info->chunk_size;
-               ecc->layout = &ecc_layout_4KB_bch8bit;
+               mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops);
                ecc->strength = 16;
        } else {
                dev_err(&info->pdev->dev,
@@ -1651,6 +1674,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
        if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
                nand_writel(info, NDECCCTRL, 0x0);
 
+       if (pdata->flash_bbt)
+               chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+       chip->ecc.strength = pdata->ecc_strength;
+       chip->ecc.size = pdata->ecc_step_size;
+
        if (nand_scan_ident(mtd, 1, NULL))
                return -ENODEV;
 
@@ -1663,13 +1692,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
                }
        }
 
-       if (pdata->flash_bbt) {
+       if (chip->bbt_options & NAND_BBT_USE_FLASH) {
                /*
                 * We'll use a bad block table stored in-flash and don't
                 * allow writing the bad block marker to the flash.
                 */
-               chip->bbt_options |= NAND_BBT_USE_FLASH |
-                                    NAND_BBT_NO_OOB_BBM;
+               chip->bbt_options |= NAND_BBT_NO_OOB_BBM;
                chip->bbt_td = &bbt_main_descr;
                chip->bbt_md = &bbt_mirror_descr;
        }
@@ -1689,10 +1717,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
                }
        }
 
-       if (pdata->ecc_strength && pdata->ecc_step_size) {
-               ecc_strength = pdata->ecc_strength;
-               ecc_step = pdata->ecc_step_size;
-       } else {
+       ecc_strength = chip->ecc.strength;
+       ecc_step = chip->ecc.size;
+       if (!ecc_strength || !ecc_step) {
                ecc_strength = chip->ecc_strength_ds;
                ecc_step = chip->ecc_step_ds;
        }
@@ -1703,7 +1730,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
                ecc_step = 512;
        }
 
-       ret = pxa_ecc_init(info, &chip->ecc, ecc_strength,
+       ret = pxa_ecc_init(info, mtd, ecc_strength,
                           ecc_step, mtd->writesize);
        if (ret)
                return ret;
@@ -1903,15 +1930,6 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
        if (of_get_property(np, "marvell,nand-keep-config", NULL))
                pdata->keep_config = 1;
        of_property_read_u32(np, "num-cs", &pdata->num_cs);
-       pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
-
-       pdata->ecc_strength = of_get_nand_ecc_strength(np);
-       if (pdata->ecc_strength < 0)
-               pdata->ecc_strength = 0;
-
-       pdata->ecc_step_size = of_get_nand_ecc_step_size(np);
-       if (pdata->ecc_step_size < 0)
-               pdata->ecc_step_size = 0;
 
        pdev->dev.platform_data = pdata;
 
index f550a57..de7d28e 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/mtd/partitions.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/of_mtd.h>
 #include <linux/delay.h>
 
 /* NANDc reg offsets */
@@ -1437,7 +1436,6 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
        struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
        struct nand_ecc_ctrl *ecc = &chip->ecc;
        u8 *oob = chip->oob_poi;
-       int free_boff;
        int data_size, oob_size;
        int ret, status = 0;
 
@@ -1451,12 +1449,11 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
 
        /* calculate the data and oob size for the last codeword/step */
        data_size = ecc->size - ((ecc->steps - 1) << 2);
-       oob_size = ecc->steps << 2;
-
-       free_boff = ecc->layout->oobfree[0].offset;
+       oob_size = mtd->oobavail;
 
        /* override new oob content to last codeword */
-       memcpy(nandc->data_buffer + data_size, oob + free_boff, oob_size);
+       mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob,
+                                   0, mtd->oobavail);
 
        set_address(host, host->cw_size * (ecc->steps - 1), page);
        update_rw_regs(host, 1, false);
@@ -1710,61 +1707,52 @@ static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr)
  * This layout is read as is when ECC is disabled. When ECC is enabled, the
  * inaccessible Bad Block byte(s) are ignored when we write to a page/oob,
  * and assumed as 0xffs when we read a page/oob. The ECC, unused and
- * dummy/real bad block bytes are grouped as ecc bytes in nand_ecclayout (i.e,
- * ecc->bytes is the sum of the three).
+ * dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is
+ * the sum of the three).
  */
-
-static struct nand_ecclayout *
-qcom_nand_create_layout(struct qcom_nand_host *host)
+static int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *oobregion)
 {
-       struct nand_chip *chip = &host->chip;
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct qcom_nand_host *host = to_qcom_nand_host(chip);
        struct nand_ecc_ctrl *ecc = &chip->ecc;
-       struct nand_ecclayout *layout;
-       int i, j, steps, pos = 0, shift = 0;
 
-       layout = devm_kzalloc(nandc->dev, sizeof(*layout), GFP_KERNEL);
-       if (!layout)
-               return NULL;
-
-       steps = mtd->writesize / ecc->size;
-       layout->eccbytes = steps * ecc->bytes;
+       if (section > 1)
+               return -ERANGE;
 
-       layout->oobfree[0].offset = (steps - 1) * ecc->bytes + host->bbm_size;
-       layout->oobfree[0].length = steps << 2;
-
-       /*
-        * the oob bytes in the first n - 1 codewords are all grouped together
-        * in the format:
-        * DUMMY_BBM + UNUSED + ECC
-        */
-       for (i = 0; i < steps - 1; i++) {
-               for (j = 0; j < ecc->bytes; j++)
-                       layout->eccpos[pos++] = i * ecc->bytes + j;
+       if (!section) {
+               oobregion->length = (ecc->bytes * (ecc->steps - 1)) +
+                                   host->bbm_size;
+               oobregion->offset = 0;
+       } else {
+               oobregion->length = host->ecc_bytes_hw + host->spare_bytes;
+               oobregion->offset = mtd->oobsize - oobregion->length;
        }
 
-       /*
-        * the oob bytes in the last codeword are grouped in the format:
-        * BBM + FREE OOB + UNUSED + ECC
-        */
+       return 0;
+}
 
-       /* fill up the bbm positions */
-       for (j = 0; j < host->bbm_size; j++)
-               layout->eccpos[pos++] = i * ecc->bytes + j;
+static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section,
+                                    struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct qcom_nand_host *host = to_qcom_nand_host(chip);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
 
-       /*
-        * fill up the ecc and reserved positions, their indices are offseted
-        * by the free oob region
-        */
-       shift = layout->oobfree[0].length + host->bbm_size;
+       if (section)
+               return -ERANGE;
 
-       for (j = 0; j < (host->ecc_bytes_hw + host->spare_bytes); j++)
-               layout->eccpos[pos++] = i * ecc->bytes + shift + j;
+       oobregion->length = ecc->steps * 4;
+       oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size;
 
-       return layout;
+       return 0;
 }
 
+static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = {
+       .ecc = qcom_nand_ooblayout_ecc,
+       .free = qcom_nand_ooblayout_free,
+};
+
 static int qcom_nand_host_setup(struct qcom_nand_host *host)
 {
        struct nand_chip *chip = &host->chip;
@@ -1851,9 +1839,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
 
        ecc->mode = NAND_ECC_HW;
 
-       ecc->layout = qcom_nand_create_layout(host);
-       if (!ecc->layout)
-               return -ENOMEM;
+       mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
 
        cwperpage = mtd->writesize / ecc->size;
 
index 9c9397b..d9309cf 100644 (file)
 
 /* new oob placement block for use with hardware ecc generation
  */
+static int s3c2410_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 0;
+       oobregion->length = 3;
+
+       return 0;
+}
+
+static int s3c2410_ooblayout_free(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 8;
+       oobregion->length = 8;
+
+       return 0;
+}
 
-static struct nand_ecclayout nand_hw_eccoob = {
-       .eccbytes = 3,
-       .eccpos = {0, 1, 2},
-       .oobfree = {{8, 8}}
+static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = {
+       .ecc = s3c2410_ooblayout_ecc,
+       .free = s3c2410_ooblayout_free,
 };
 
 /* controller and mtd information */
@@ -542,7 +564,8 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
        diff0 |= (diff1 << 8);
        diff0 |= (diff2 << 16);
 
-       if ((diff0 & ~(1<<fls(diff0))) == 0)
+       /* equal to "(diff0 & ~(1 << __ffs(diff0)))" */
+       if ((diff0 & (diff0 - 1)) == 0)
                return 1;
 
        return -1;
@@ -859,6 +882,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
        }
 #else
        chip->ecc.mode      = NAND_ECC_SOFT;
+       chip->ecc.algo  = NAND_ECC_HAMMING;
 #endif
 
        if (set->disable_ecc)
@@ -919,7 +943,7 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
        } else {
                chip->ecc.size      = 512;
                chip->ecc.bytes     = 3;
-               chip->ecc.layout    = &nand_hw_eccoob;
+               mtd_set_ooblayout(nand_to_mtd(chip), &s3c2410_ooblayout_ops);
        }
 }
 
index 4814402..6fa3bcd 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/of_mtd.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/sh_dma.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/sh_flctl.h>
 
-static struct nand_ecclayout flctl_4secc_oob_16 = {
-       .eccbytes = 10,
-       .eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
-       .oobfree = {
-               {.offset = 12,
-               . length = 4} },
+static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section,
+                                       struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 0;
+       oobregion->length = chip->ecc.bytes;
+
+       return 0;
+}
+
+static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section,
+                                        struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->offset = 12;
+       oobregion->length = 4;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = {
+       .ecc = flctl_4secc_ooblayout_sp_ecc,
+       .free = flctl_4secc_ooblayout_sp_free,
 };
 
-static struct nand_ecclayout flctl_4secc_oob_64 = {
-       .eccbytes = 4 * 10,
-       .eccpos = {
-                6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
-               22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-               38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-               54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
-       .oobfree = {
-               {.offset =  2, .length = 4},
-               {.offset = 16, .length = 6},
-               {.offset = 32, .length = 6},
-               {.offset = 48, .length = 6} },
+static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section,
+                                       struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section >= chip->ecc.steps)
+               return -ERANGE;
+
+       oobregion->offset = (section * 16) + 6;
+       oobregion->length = chip->ecc.bytes;
+
+       return 0;
+}
+
+static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section,
+                                        struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
+       if (section >= chip->ecc.steps)
+               return -ERANGE;
+
+       oobregion->offset = section * 16;
+       oobregion->length = 6;
+
+       if (!section) {
+               oobregion->offset += 2;
+               oobregion->length -= 2;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = {
+       .ecc = flctl_4secc_ooblayout_lp_ecc,
+       .free = flctl_4secc_ooblayout_lp_free,
 };
 
 static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
@@ -987,10 +1033,10 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
 
        if (flctl->hwecc) {
                if (mtd->writesize == 512) {
-                       chip->ecc.layout = &flctl_4secc_oob_16;
+                       mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops);
                        chip->badblock_pattern = &flctl_4secc_smallpage;
                } else {
-                       chip->ecc.layout = &flctl_4secc_oob_64;
+                       mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops);
                        chip->badblock_pattern = &flctl_4secc_largepage;
                }
 
@@ -1005,6 +1051,7 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
                flctl->flcmncr_base |= _4ECCEN;
        } else {
                chip->ecc.mode = NAND_ECC_SOFT;
+               chip->ecc.algo = NAND_ECC_HAMMING;
        }
 
        return 0;
@@ -1044,8 +1091,6 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
        const struct of_device_id *match;
        struct flctl_soc_config *config;
        struct sh_flctl_platform_data *pdata;
-       struct device_node *dn = dev->of_node;
-       int ret;
 
        match = of_match_device(of_flctl_match, dev);
        if (match)
@@ -1065,15 +1110,6 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
        pdata->has_hwecc = config->has_hwecc;
        pdata->use_holden = config->use_holden;
 
-       /* parse user defined options */
-       ret = of_get_nand_bus_width(dn);
-       if (ret == 16)
-               pdata->flcmncr_val |= SEL_16BIT;
-       else if (ret != 8) {
-               dev_err(dev, "%s: invalid bus width\n", __func__);
-               return NULL;
-       }
-
        return pdata;
 }
 
@@ -1136,15 +1172,14 @@ static int flctl_probe(struct platform_device *pdev)
        nand->chip_delay = 20;
 
        nand->read_byte = flctl_read_byte;
+       nand->read_word = flctl_read_word;
        nand->write_buf = flctl_write_buf;
        nand->read_buf = flctl_read_buf;
        nand->select_chip = flctl_select_chip;
        nand->cmdfunc = flctl_cmdfunc;
 
-       if (pdata->flcmncr_val & SEL_16BIT) {
+       if (pdata->flcmncr_val & SEL_16BIT)
                nand->options |= NAND_BUSWIDTH_16;
-               nand->read_word = flctl_read_word;
-       }
 
        pm_runtime_enable(&pdev->dev);
        pm_runtime_resume(&pdev->dev);
@@ -1155,6 +1190,16 @@ static int flctl_probe(struct platform_device *pdev)
        if (ret)
                goto err_chip;
 
+       if (nand->options & NAND_BUSWIDTH_16) {
+               /*
+                * NAND_BUSWIDTH_16 may have been set by nand_scan_ident().
+                * Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign
+                * flctl->flcmncr_base to pdata->flcmncr_val.
+                */
+               pdata->flcmncr_val |= SEL_16BIT;
+               flctl->flcmncr_base = pdata->flcmncr_val;
+       }
+
        ret = flctl_chip_init_tail(flctl_mtd);
        if (ret)
                goto err_chip;
index b7d1b55..064ca17 100644 (file)
@@ -148,6 +148,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
        /* Link the private data with the MTD structure */
        mtd = nand_to_mtd(this);
        mtd->dev.parent = &pdev->dev;
+       mtd_set_ooblayout(mtd, data->ecc_layout);
 
        platform_set_drvdata(pdev, sharpsl);
 
@@ -170,7 +171,6 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
        this->ecc.bytes = 3;
        this->ecc.strength = 1;
        this->badblock_pattern = data->badblock_pattern;
-       this->ecc.layout = data->ecc_layout;
        this->ecc.hwctl = sharpsl_nand_enable_hwecc;
        this->ecc.calculate = sharpsl_nand_calculate_ecc;
        this->ecc.correct = nand_correct_data;
index c514740..5939dff 100644 (file)
 #include <linux/sizes.h>
 #include "sm_common.h"
 
-static struct nand_ecclayout nand_oob_sm = {
-       .eccbytes = 6,
-       .eccpos = {8, 9, 10, 13, 14, 15},
-       .oobfree = {
-               {.offset = 0 , .length = 4}, /* reserved */
-               {.offset = 6 , .length = 2}, /* LBA1 */
-               {.offset = 11, .length = 2}  /* LBA2 */
+static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section,
+                               struct mtd_oob_region *oobregion)
+{
+       if (section > 1)
+               return -ERANGE;
+
+       oobregion->length = 3;
+       oobregion->offset = ((section + 1) * 8) - 3;
+
+       return 0;
+}
+
+static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       switch (section) {
+       case 0:
+               /* reserved */
+               oobregion->offset = 0;
+               oobregion->length = 4;
+               break;
+       case 1:
+               /* LBA1 */
+               oobregion->offset = 6;
+               oobregion->length = 2;
+               break;
+       case 2:
+               /* LBA2 */
+               oobregion->offset = 11;
+               oobregion->length = 2;
+               break;
+       default:
+               return -ERANGE;
        }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops oob_sm_ops = {
+       .ecc = oob_sm_ooblayout_ecc,
+       .free = oob_sm_ooblayout_free,
 };
 
 /* NOTE: This layout is is not compatabable with SmartMedia, */
@@ -28,15 +61,43 @@ static struct nand_ecclayout nand_oob_sm = {
 /* If you use smftl, it will bypass this and work correctly */
 /* If you not, then you break SmartMedia compliance anyway */
 
-static struct nand_ecclayout nand_oob_sm_small = {
-       .eccbytes = 3,
-       .eccpos = {0, 1, 2},
-       .oobfree = {
-               {.offset = 3 , .length = 2}, /* reserved */
-               {.offset = 6 , .length = 2}, /* LBA1 */
+static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                     struct mtd_oob_region *oobregion)
+{
+       if (section)
+               return -ERANGE;
+
+       oobregion->length = 3;
+       oobregion->offset = 0;
+
+       return 0;
+}
+
+static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section,
+                                      struct mtd_oob_region *oobregion)
+{
+       switch (section) {
+       case 0:
+               /* reserved */
+               oobregion->offset = 3;
+               oobregion->length = 2;
+               break;
+       case 1:
+               /* LBA1 */
+               oobregion->offset = 6;
+               oobregion->length = 2;
+               break;
+       default:
+               return -ERANGE;
        }
-};
 
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops oob_sm_small_ops = {
+       .ecc = oob_sm_small_ooblayout_ecc,
+       .free = oob_sm_small_ooblayout_free,
+};
 
 static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
@@ -121,9 +182,9 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia)
 
        /* ECC layout */
        if (mtd->writesize == SM_SECTOR_SIZE)
-               chip->ecc.layout = &nand_oob_sm;
+               mtd_set_ooblayout(mtd, &oob_sm_ops);
        else if (mtd->writesize == SM_SMALL_PAGE)
-               chip->ecc.layout = &nand_oob_sm_small;
+               mtd_set_ooblayout(mtd, &oob_sm_small_ops);
        else
                return -ENODEV;
 
index e3305f9..888fd31 100644 (file)
@@ -180,6 +180,7 @@ static int socrates_nand_probe(struct platform_device *ofdev)
        nand_chip->dev_ready = socrates_nand_device_ready;
 
        nand_chip->ecc.mode = NAND_ECC_SOFT;    /* enable ECC */
+       nand_chip->ecc.algo = NAND_ECC_HAMMING;
 
        /* TODO: I have no idea what real delay is. */
        nand_chip->chip_delay = 20;             /* 20us command delay time */
index 1c03eee..a83a690 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
-#include <linux/of_mtd.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
@@ -39,7 +38,7 @@
 #include <linux/dmaengine.h>
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
-#include <linux/io.h>
+#include <linux/iopoll.h>
 
 #define NFC_REG_CTL            0x0000
 #define NFC_REG_ST             0x0004
 /* define bit use in NFC_ECC_ST */
 #define NFC_ECC_ERR(x)         BIT(x)
 #define NFC_ECC_PAT_FOUND(x)   BIT(x + 16)
-#define NFC_ECC_ERR_CNT(b, x)  (((x) >> ((b) * 8)) & 0xff)
+#define NFC_ECC_ERR_CNT(b, x)  (((x) >> (((b) % 4) * 8)) & 0xff)
 
 #define NFC_DEFAULT_TIMEOUT_MS 1000
 
@@ -212,12 +211,9 @@ struct sunxi_nand_chip_sel {
  * sunxi HW ECC infos: stores information related to HW ECC support
  *
  * @mode:      the sunxi ECC mode field deduced from ECC requirements
- * @layout:    the OOB layout depending on the ECC requirements and the
- *             selected ECC mode
  */
 struct sunxi_nand_hw_ecc {
        int mode;
-       struct nand_ecclayout layout;
 };
 
 /*
@@ -239,6 +235,10 @@ struct sunxi_nand_chip {
        u32 timing_cfg;
        u32 timing_ctl;
        int selected;
+       int addr_cycles;
+       u32 addr[2];
+       int cmd_cycles;
+       u8 cmd[2];
        int nsels;
        struct sunxi_nand_chip_sel sels[0];
 };
@@ -298,54 +298,71 @@ static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
-                             unsigned int timeout_ms)
+static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
+                                bool use_polling, unsigned int timeout_ms)
 {
-       init_completion(&nfc->complete);
+       int ret;
 
-       writel(flags, nfc->regs + NFC_REG_INT);
+       if (events & ~NFC_INT_MASK)
+               return -EINVAL;
 
        if (!timeout_ms)
                timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
 
-       if (!wait_for_completion_timeout(&nfc->complete,
-                                        msecs_to_jiffies(timeout_ms))) {
-               dev_err(nfc->dev, "wait interrupt timedout\n");
-               return -ETIMEDOUT;
+       if (!use_polling) {
+               init_completion(&nfc->complete);
+
+               writel(events, nfc->regs + NFC_REG_INT);
+
+               ret = wait_for_completion_timeout(&nfc->complete,
+                                               msecs_to_jiffies(timeout_ms));
+
+               writel(0, nfc->regs + NFC_REG_INT);
+       } else {
+               u32 status;
+
+               ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
+                                        (status & events) == events, 1,
+                                        timeout_ms * 1000);
        }
 
-       return 0;
+       writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
+
+       if (ret)
+               dev_err(nfc->dev, "wait interrupt timedout\n");
+
+       return ret;
 }
 
 static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
 {
-       unsigned long timeout = jiffies +
-                               msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS);
+       u32 status;
+       int ret;
 
-       do {
-               if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
-                       return 0;
-       } while (time_before(jiffies, timeout));
+       ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
+                                !(status & NFC_CMD_FIFO_STATUS), 1,
+                                NFC_DEFAULT_TIMEOUT_MS * 1000);
+       if (ret)
+               dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
 
-       dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
-       return -ETIMEDOUT;
+       return ret;
 }
 
 static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
 {
-       unsigned long timeout = jiffies +
-                               msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS);
+       u32 ctl;
+       int ret;
 
        writel(0, nfc->regs + NFC_REG_ECC_CTL);
        writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
 
-       do {
-               if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET))
-                       return 0;
-       } while (time_before(jiffies, timeout));
+       ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl,
+                                !(ctl & NFC_RESET), 1,
+                                NFC_DEFAULT_TIMEOUT_MS * 1000);
+       if (ret)
+               dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
 
-       dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
-       return -ETIMEDOUT;
+       return ret;
 }
 
 static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
@@ -354,7 +371,6 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
        struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
        struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
        struct sunxi_nand_rb *rb;
-       unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20);
        int ret;
 
        if (sunxi_nand->selected < 0)
@@ -364,12 +380,6 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
 
        switch (rb->type) {
        case RB_NATIVE:
-               ret = !!(readl(nfc->regs + NFC_REG_ST) &
-                        NFC_RB_STATE(rb->info.nativeid));
-               if (ret)
-                       break;
-
-               sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
                ret = !!(readl(nfc->regs + NFC_REG_ST) &
                         NFC_RB_STATE(rb->info.nativeid));
                break;
@@ -407,7 +417,7 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
                sel = &sunxi_nand->sels[chip];
 
                ctl |= NFC_CE_SEL(sel->cs) | NFC_EN |
-                      NFC_PAGE_SHIFT(nand->page_shift - 10);
+                      NFC_PAGE_SHIFT(nand->page_shift);
                if (sel->rb.type == RB_NONE) {
                        nand->dev_ready = NULL;
                } else {
@@ -452,7 +462,7 @@ static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
                tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
                writel(tmp, nfc->regs + NFC_REG_CMD);
 
-               ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+               ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
                if (ret)
                        break;
 
@@ -487,7 +497,7 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
                      NFC_ACCESS_DIR;
                writel(tmp, nfc->regs + NFC_REG_CMD);
 
-               ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+               ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
                if (ret)
                        break;
 
@@ -511,32 +521,54 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
        struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
        struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
        int ret;
-       u32 tmp;
 
        ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
        if (ret)
                return;
 
-       if (ctrl & NAND_CTRL_CHANGE) {
-               tmp = readl(nfc->regs + NFC_REG_CTL);
-               if (ctrl & NAND_NCE)
-                       tmp |= NFC_CE_CTL;
-               else
-                       tmp &= ~NFC_CE_CTL;
-               writel(tmp, nfc->regs + NFC_REG_CTL);
-       }
+       if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) &&
+           !(ctrl & (NAND_CLE | NAND_ALE))) {
+               u32 cmd = 0;
 
-       if (dat == NAND_CMD_NONE)
-               return;
+               if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles)
+                       return;
 
-       if (ctrl & NAND_CLE) {
-               writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD);
-       } else {
-               writel(dat, nfc->regs + NFC_REG_ADDR_LOW);
-               writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD);
+               if (sunxi_nand->cmd_cycles--)
+                       cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0];
+
+               if (sunxi_nand->cmd_cycles--) {
+                       cmd |= NFC_SEND_CMD2;
+                       writel(sunxi_nand->cmd[1],
+                              nfc->regs + NFC_REG_RCMD_SET);
+               }
+
+               sunxi_nand->cmd_cycles = 0;
+
+               if (sunxi_nand->addr_cycles) {
+                       cmd |= NFC_SEND_ADR |
+                              NFC_ADR_NUM(sunxi_nand->addr_cycles);
+                       writel(sunxi_nand->addr[0],
+                              nfc->regs + NFC_REG_ADDR_LOW);
+               }
+
+               if (sunxi_nand->addr_cycles > 4)
+                       writel(sunxi_nand->addr[1],
+                              nfc->regs + NFC_REG_ADDR_HIGH);
+
+               writel(cmd, nfc->regs + NFC_REG_CMD);
+               sunxi_nand->addr[0] = 0;
+               sunxi_nand->addr[1] = 0;
+               sunxi_nand->addr_cycles = 0;
+               sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
        }
 
-       sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+       if (ctrl & NAND_CLE) {
+               sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat;
+       } else if (ctrl & NAND_ALE) {
+               sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |=
+                               dat << ((sunxi_nand->addr_cycles % 4) * 8);
+               sunxi_nand->addr_cycles++;
+       }
 }
 
 /* These seed values have been extracted from Allwinner's BSP */
@@ -717,7 +749,8 @@ static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
        ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
        ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
                     NFC_ECC_BLOCK_SIZE_MSK);
-       ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION;
+       ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION |
+                  NFC_ECC_PIPELINE;
 
        writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
 }
@@ -739,18 +772,106 @@ static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
        buf[3] = user_data >> 24;
 }
 
+static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
+{
+       return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob,
+                                               int step, bool bbm, int page)
+{
+       struct nand_chip *nand = mtd_to_nand(mtd);
+       struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+       sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
+                                  oob);
+
+       /* De-randomize the Bad Block Marker. */
+       if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
+               sunxi_nfc_randomize_bbm(mtd, page, oob);
+}
+
+static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd,
+                                               const u8 *oob, int step,
+                                               bool bbm, int page)
+{
+       struct nand_chip *nand = mtd_to_nand(mtd);
+       struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+       u8 user_data[4];
+
+       /* Randomize the Bad Block Marker. */
+       if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
+               memcpy(user_data, oob, sizeof(user_data));
+               sunxi_nfc_randomize_bbm(mtd, page, user_data);
+               oob = user_data;
+       }
+
+       writel(sunxi_nfc_buf_to_user_data(oob),
+              nfc->regs + NFC_REG_USER_DATA(step));
+}
+
+static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd,
+                                         unsigned int *max_bitflips, int ret)
+{
+       if (ret < 0) {
+               mtd->ecc_stats.failed++;
+       } else {
+               mtd->ecc_stats.corrected += ret;
+               *max_bitflips = max_t(unsigned int, *max_bitflips, ret);
+       }
+}
+
+static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob,
+                                   int step, bool *erased)
+{
+       struct nand_chip *nand = mtd_to_nand(mtd);
+       struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+       struct nand_ecc_ctrl *ecc = &nand->ecc;
+       u32 status, tmp;
+
+       *erased = false;
+
+       status = readl(nfc->regs + NFC_REG_ECC_ST);
+
+       if (status & NFC_ECC_ERR(step))
+               return -EBADMSG;
+
+       if (status & NFC_ECC_PAT_FOUND(step)) {
+               u8 pattern;
+
+               if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
+                       pattern = 0x0;
+               } else {
+                       pattern = 0xff;
+                       *erased = true;
+               }
+
+               if (data)
+                       memset(data, pattern, ecc->size);
+
+               if (oob)
+                       memset(oob, pattern, ecc->bytes + 4);
+
+               return 0;
+       }
+
+       tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
+
+       return NFC_ECC_ERR_CNT(step, tmp);
+}
+
 static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
                                       u8 *data, int data_off,
                                       u8 *oob, int oob_off,
                                       int *cur_off,
                                       unsigned int *max_bitflips,
-                                      bool bbm, int page)
+                                      bool bbm, bool oob_required, int page)
 {
        struct nand_chip *nand = mtd_to_nand(mtd);
        struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
        struct nand_ecc_ctrl *ecc = &nand->ecc;
        int raw_mode = 0;
-       u32 status;
+       bool erased;
        int ret;
 
        if (*cur_off != data_off)
@@ -769,34 +890,19 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
        writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
               nfc->regs + NFC_REG_CMD);
 
-       ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+       ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
        sunxi_nfc_randomizer_disable(mtd);
        if (ret)
                return ret;
 
        *cur_off = oob_off + ecc->bytes + 4;
 
-       status = readl(nfc->regs + NFC_REG_ECC_ST);
-       if (status & NFC_ECC_PAT_FOUND(0)) {
-               u8 pattern = 0xff;
-
-               if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1)))
-                       pattern = 0x0;
-
-               memset(data, pattern, ecc->size);
-               memset(oob, pattern, ecc->bytes + 4);
-
+       ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0,
+                                      &erased);
+       if (erased)
                return 1;
-       }
-
-       ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)));
-
-       memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
-
-       nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
-       sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page);
 
-       if (status & NFC_ECC_ERR(0)) {
+       if (ret < 0) {
                /*
                 * Re-read the data with the randomizer disabled to identify
                 * bitflips in erased pages.
@@ -804,35 +910,34 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
                if (nand->options & NAND_NEED_SCRAMBLING) {
                        nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
                        nand->read_buf(mtd, data, ecc->size);
-                       nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
-                       nand->read_buf(mtd, oob, ecc->bytes + 4);
+               } else {
+                       memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
+                                     ecc->size);
                }
 
+               nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
+               nand->read_buf(mtd, oob, ecc->bytes + 4);
+
                ret = nand_check_erased_ecc_chunk(data, ecc->size,
                                                  oob, ecc->bytes + 4,
                                                  NULL, 0, ecc->strength);
                if (ret >= 0)
                        raw_mode = 1;
        } else {
-               /*
-                * The engine protects 4 bytes of OOB data per chunk.
-                * Retrieve the corrected OOB bytes.
-                */
-               sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)),
-                                          oob);
+               memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
 
-               /* De-randomize the Bad Block Marker. */
-               if (bbm && nand->options & NAND_NEED_SCRAMBLING)
-                       sunxi_nfc_randomize_bbm(mtd, page, oob);
-       }
+               if (oob_required) {
+                       nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
+                       sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4,
+                                                     true, page);
 
-       if (ret < 0) {
-               mtd->ecc_stats.failed++;
-       } else {
-               mtd->ecc_stats.corrected += ret;
-               *max_bitflips = max_t(unsigned int, *max_bitflips, ret);
+                       sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0,
+                                                           bbm, page);
+               }
        }
 
+       sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret);
+
        return raw_mode;
 }
 
@@ -848,7 +953,7 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
        if (len <= 0)
                return;
 
-       if (*cur_off != offset)
+       if (!cur_off || *cur_off != offset)
                nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
                              offset + mtd->writesize, -1);
 
@@ -858,12 +963,8 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
                sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len,
                                              false, page);
 
-       *cur_off = mtd->oobsize + mtd->writesize;
-}
-
-static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
-{
-       return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+       if (cur_off)
+               *cur_off = mtd->oobsize + mtd->writesize;
 }
 
 static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
@@ -882,19 +983,6 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
 
        sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
 
-       /* Fill OOB data in */
-       if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
-               u8 user_data[4];
-
-               memcpy(user_data, oob, 4);
-               sunxi_nfc_randomize_bbm(mtd, page, user_data);
-               writel(sunxi_nfc_buf_to_user_data(user_data),
-                      nfc->regs + NFC_REG_USER_DATA(0));
-       } else {
-               writel(sunxi_nfc_buf_to_user_data(oob),
-                      nfc->regs + NFC_REG_USER_DATA(0));
-       }
-
        if (data_off + ecc->size != oob_off)
                nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);
 
@@ -903,11 +991,13 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
                return ret;
 
        sunxi_nfc_randomizer_enable(mtd);
+       sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page);
+
        writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
               NFC_ACCESS_DIR | NFC_ECC_OP,
               nfc->regs + NFC_REG_CMD);
 
-       ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+       ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
        sunxi_nfc_randomizer_disable(mtd);
        if (ret)
                return ret;
@@ -929,13 +1019,14 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
        if (len <= 0)
                return;
 
-       if (*cur_off != offset)
+       if (!cur_off || *cur_off != offset)
                nand->cmdfunc(mtd, NAND_CMD_RNDIN,
                              offset + mtd->writesize, -1);
 
        sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page);
 
-       *cur_off = mtd->oobsize + mtd->writesize;
+       if (cur_off)
+               *cur_off = mtd->oobsize + mtd->writesize;
 }
 
 static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
@@ -958,7 +1049,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
                ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
                                                  oob_off + mtd->writesize,
                                                  &cur_off, &max_bitflips,
-                                                 !i, page);
+                                                 !i, oob_required, page);
                if (ret < 0)
                        return ret;
                else if (ret)
@@ -974,6 +1065,39 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
        return max_bitflips;
 }
 
+static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
+                                        struct nand_chip *chip,
+                                        u32 data_offs, u32 readlen,
+                                        u8 *bufpoi, int page)
+{
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+       int ret, i, cur_off = 0;
+       unsigned int max_bitflips = 0;
+
+       sunxi_nfc_hw_ecc_enable(mtd);
+
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+       for (i = data_offs / ecc->size;
+            i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
+               int data_off = i * ecc->size;
+               int oob_off = i * (ecc->bytes + 4);
+               u8 *data = bufpoi + data_off;
+               u8 *oob = chip->oob_poi + oob_off;
+
+               ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off,
+                                                 oob,
+                                                 oob_off + mtd->writesize,
+                                                 &cur_off, &max_bitflips, !i,
+                                                 false, page);
+               if (ret < 0)
+                       return ret;
+       }
+
+       sunxi_nfc_hw_ecc_disable(mtd);
+
+       return max_bitflips;
+}
+
 static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
                                       struct nand_chip *chip,
                                       const uint8_t *buf, int oob_required,
@@ -1026,7 +1150,9 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
 
                ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
                                                  oob_off, &cur_off,
-                                                 &max_bitflips, !i, page);
+                                                 &max_bitflips, !i,
+                                                 oob_required,
+                                                 page);
                if (ret < 0)
                        return ret;
                else if (ret)
@@ -1074,6 +1200,40 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
        return 0;
 }
 
+static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd,
+                                           struct nand_chip *chip,
+                                           int page)
+{
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+       chip->pagebuf = -1;
+
+       return chip->ecc.read_page(mtd, chip, chip->buffers->databuf, 1, page);
+}
+
+static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd,
+                                            struct nand_chip *chip,
+                                            int page)
+{
+       int ret, status;
+
+       chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
+
+       chip->pagebuf = -1;
+
+       memset(chip->buffers->databuf, 0xff, mtd->writesize);
+       ret = chip->ecc.write_page(mtd, chip, chip->buffers->databuf, 1, page);
+       if (ret)
+               return ret;
+
+       /* Send command to program the OOB data */
+       chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+       status = chip->waitfunc(mtd, chip);
+
+       return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
 static const s32 tWB_lut[] = {6, 12, 16, 20};
 static const s32 tRHW_lut[] = {4, 8, 12, 20};
 
@@ -1101,6 +1261,7 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
        struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
        u32 min_clk_period = 0;
        s32 tWB, tADL, tWHR, tRHW, tCAD;
+       long real_clk_rate;
 
        /* T1 <=> tCLS */
        if (timings->tCLS_min > min_clk_period)
@@ -1163,6 +1324,18 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
                min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
 
        /* T16 - T19 + tCAD */
+       if (timings->tWB_max > (min_clk_period * 20))
+               min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20);
+
+       if (timings->tADL_min > (min_clk_period * 32))
+               min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
+
+       if (timings->tWHR_min > (min_clk_period * 32))
+               min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
+
+       if (timings->tRHW_min > (min_clk_period * 20))
+               min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20);
+
        tWB  = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
                                        min_clk_period);
        if (tWB < 0) {
@@ -1198,23 +1371,26 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
        /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
        chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
 
-       /*
-        * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
-        * output cycle timings shall be used if the host drives tRC less than
-        * 30 ns.
-        */
-       chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0;
-
        /* Convert min_clk_period from picoseconds to nanoseconds */
        min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
 
        /*
-        * Convert min_clk_period into a clk frequency, then get the
-        * appropriate rate for the NAND controller IP given this formula
-        * (specified in the datasheet):
-        * nand clk_rate = 2 * min_clk_rate
+        * Unlike what is stated in Allwinner datasheet, the clk_rate should
+        * be set to (1 / min_clk_period), and not (2 / min_clk_period).
+        * This new formula was verified with a scope and validated by
+        * Allwinner engineers.
         */
-       chip->clk_rate = (2 * NSEC_PER_SEC) / min_clk_period;
+       chip->clk_rate = NSEC_PER_SEC / min_clk_period;
+       real_clk_rate = clk_round_rate(nfc->mod_clk, chip->clk_rate);
+
+       /*
+        * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
+        * output cycle timings shall be used if the host drives tRC less than
+        * 30 ns.
+        */
+       min_clk_period = NSEC_PER_SEC / real_clk_rate;
+       chip->timing_ctl = ((min_clk_period * 2) < 30) ?
+                          NFC_TIMING_CTL_EDO : 0;
 
        return 0;
 }
@@ -1257,6 +1433,57 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
        return sunxi_nand_chip_set_timings(chip, timings);
 }
 
+static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                   struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *nand = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &nand->ecc;
+
+       if (section >= ecc->steps)
+               return -ERANGE;
+
+       oobregion->offset = section * (ecc->bytes + 4) + 4;
+       oobregion->length = ecc->bytes;
+
+       return 0;
+}
+
+static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
+                                    struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *nand = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &nand->ecc;
+
+       if (section > ecc->steps)
+               return -ERANGE;
+
+       /*
+        * The first 2 bytes are used for BB markers, hence we
+        * only have 2 bytes available in the first user data
+        * section.
+        */
+       if (!section && ecc->mode == NAND_ECC_HW) {
+               oobregion->offset = 2;
+               oobregion->length = 2;
+
+               return 0;
+       }
+
+       oobregion->offset = section * (ecc->bytes + 4);
+
+       if (section < ecc->steps)
+               oobregion->length = 4;
+       else
+               oobregion->offset = mtd->oobsize - oobregion->offset;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
+       .ecc = sunxi_nand_ooblayout_ecc,
+       .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)
@@ -1266,7 +1493,6 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
        struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
        struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
        struct sunxi_nand_hw_ecc *data;
-       struct nand_ecclayout *layout;
        int nsectors;
        int ret;
        int i;
@@ -1295,7 +1521,6 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
        /* HW ECC always work with even numbers of ECC bytes */
        ecc->bytes = ALIGN(ecc->bytes, 2);
 
-       layout = &data->layout;
        nsectors = mtd->writesize / ecc->size;
 
        if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
@@ -1303,9 +1528,9 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
                goto err;
        }
 
-       layout->eccbytes = (ecc->bytes * nsectors);
-
-       ecc->layout = layout;
+       ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob;
+       ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob;
+       mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
        ecc->priv = data;
 
        return 0;
@@ -1325,9 +1550,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
                                       struct nand_ecc_ctrl *ecc,
                                       struct device_node *np)
 {
-       struct nand_ecclayout *layout;
-       int nsectors;
-       int i, j;
        int ret;
 
        ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
@@ -1336,40 +1558,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
 
        ecc->read_page = sunxi_nfc_hw_ecc_read_page;
        ecc->write_page = sunxi_nfc_hw_ecc_write_page;
-       layout = ecc->layout;
-       nsectors = mtd->writesize / ecc->size;
-
-       for (i = 0; i < nsectors; i++) {
-               if (i) {
-                       layout->oobfree[i].offset =
-                               layout->oobfree[i - 1].offset +
-                               layout->oobfree[i - 1].length +
-                               ecc->bytes;
-                       layout->oobfree[i].length = 4;
-               } else {
-                       /*
-                        * The first 2 bytes are used for BB markers, hence we
-                        * only have 2 bytes available in the first user data
-                        * section.
-                        */
-                       layout->oobfree[i].length = 2;
-                       layout->oobfree[i].offset = 2;
-               }
-
-               for (j = 0; j < ecc->bytes; j++)
-                       layout->eccpos[(ecc->bytes * i) + j] =
-                                       layout->oobfree[i].offset +
-                                       layout->oobfree[i].length + j;
-       }
-
-       if (mtd->oobsize > (ecc->bytes + 4) * nsectors) {
-               layout->oobfree[nsectors].offset =
-                               layout->oobfree[nsectors - 1].offset +
-                               layout->oobfree[nsectors - 1].length +
-                               ecc->bytes;
-               layout->oobfree[nsectors].length = mtd->oobsize -
-                               ((ecc->bytes + 4) * nsectors);
-       }
+       ecc->read_oob_raw = nand_read_oob_std;
+       ecc->write_oob_raw = nand_write_oob_std;
+       ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
 
        return 0;
 }
@@ -1378,9 +1569,6 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
                                                struct nand_ecc_ctrl *ecc,
                                                struct device_node *np)
 {
-       struct nand_ecclayout *layout;
-       int nsectors;
-       int i;
        int ret;
 
        ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
@@ -1390,15 +1578,8 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
        ecc->prepad = 4;
        ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;
        ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;
-
-       layout = ecc->layout;
-       nsectors = mtd->writesize / ecc->size;
-
-       for (i = 0; i < (ecc->bytes * nsectors); i++)
-               layout->eccpos[i] = i;
-
-       layout->oobfree[0].length = mtd->oobsize - i;
-       layout->oobfree[0].offset = i;
+       ecc->read_oob_raw = nand_read_oob_syndrome;
+       ecc->write_oob_raw = nand_write_oob_syndrome;
 
        return 0;
 }
@@ -1411,7 +1592,6 @@ static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
                sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc);
                break;
        case NAND_ECC_NONE:
-               kfree(ecc->layout);
        default:
                break;
        }
@@ -1432,8 +1612,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
                return -EINVAL;
 
        switch (ecc->mode) {
-       case NAND_ECC_SOFT_BCH:
-               break;
        case NAND_ECC_HW:
                ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np);
                if (ret)
@@ -1445,10 +1623,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
                        return ret;
                break;
        case NAND_ECC_NONE:
-               ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL);
-               if (!ecc->layout)
-                       return -ENOMEM;
-               ecc->layout->oobfree[0].length = mtd->oobsize;
        case NAND_ECC_SOFT:
                break;
        default:
@@ -1536,21 +1710,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
                }
        }
 
-       timings = onfi_async_timing_mode_to_sdr_timings(0);
-       if (IS_ERR(timings)) {
-               ret = PTR_ERR(timings);
-               dev_err(dev,
-                       "could not retrieve timings for ONFI mode 0: %d\n",
-                       ret);
-               return ret;
-       }
-
-       ret = sunxi_nand_chip_set_timings(chip, timings);
-       if (ret) {
-               dev_err(dev, "could not configure chip timings: %d\n", ret);
-               return ret;
-       }
-
        nand = &chip->nand;
        /* Default tR value specified in the ONFI spec (chapter 4.15.1) */
        nand->chip_delay = 200;
@@ -1570,6 +1729,21 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
        mtd = nand_to_mtd(nand);
        mtd->dev.parent = dev;
 
+       timings = onfi_async_timing_mode_to_sdr_timings(0);
+       if (IS_ERR(timings)) {
+               ret = PTR_ERR(timings);
+               dev_err(dev,
+                       "could not retrieve timings for ONFI mode 0: %d\n",
+                       ret);
+               return ret;
+       }
+
+       ret = sunxi_nand_chip_set_timings(chip, timings);
+       if (ret) {
+               dev_err(dev, "could not configure chip timings: %d\n", ret);
+               return ret;
+       }
+
        ret = nand_scan_ident(mtd, nsels, NULL);
        if (ret)
                return ret;
@@ -1580,6 +1754,8 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
        if (nand->options & NAND_NEED_SCRAMBLING)
                nand->options |= NAND_NO_SUBPAGE_WRITE;
 
+       nand->options |= NAND_SUBPAGE_READ;
+
        ret = sunxi_nand_chip_init_timings(chip, np);
        if (ret) {
                dev_err(dev, "could not configure chip timings: %d\n", ret);
@@ -1728,6 +1904,8 @@ static int sunxi_nfc_remove(struct platform_device *pdev)
        struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
 
        sunxi_nand_chips_cleanup(nfc);
+       clk_disable_unprepare(nfc->mod_clk);
+       clk_disable_unprepare(nfc->ahb_clk);
 
        return 0;
 }
index 293feb1..3ad514c 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
-#include <linux/of_mtd.h>
 #include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
@@ -175,34 +174,6 @@ static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd)
        return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip);
 }
 
-static struct nand_ecclayout vf610_nfc_ecc45 = {
-       .eccbytes = 45,
-       .eccpos = {19, 20, 21, 22, 23,
-                  24, 25, 26, 27, 28, 29, 30, 31,
-                  32, 33, 34, 35, 36, 37, 38, 39,
-                  40, 41, 42, 43, 44, 45, 46, 47,
-                  48, 49, 50, 51, 52, 53, 54, 55,
-                  56, 57, 58, 59, 60, 61, 62, 63},
-       .oobfree = {
-               {.offset = 2,
-                .length = 17} }
-};
-
-static struct nand_ecclayout vf610_nfc_ecc60 = {
-       .eccbytes = 60,
-       .eccpos = { 4,  5,  6,  7,  8,  9, 10, 11,
-                  12, 13, 14, 15, 16, 17, 18, 19,
-                  20, 21, 22, 23, 24, 25, 26, 27,
-                  28, 29, 30, 31, 32, 33, 34, 35,
-                  36, 37, 38, 39, 40, 41, 42, 43,
-                  44, 45, 46, 47, 48, 49, 50, 51,
-                  52, 53, 54, 55, 56, 57, 58, 59,
-                  60, 61, 62, 63 },
-       .oobfree = {
-               {.offset = 2,
-                .length = 2} }
-};
-
 static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg)
 {
        return readl(nfc->regs + reg);
@@ -781,14 +752,16 @@ static int vf610_nfc_probe(struct platform_device *pdev)
                if (mtd->oobsize > 64)
                        mtd->oobsize = 64;
 
+               /*
+                * mtd->ecclayout is not specified here because we're using the
+                * default large page ECC layout defined in NAND core.
+                */
                if (chip->ecc.strength == 32) {
                        nfc->ecc_mode = ECC_60_BYTE;
                        chip->ecc.bytes = 60;
-                       chip->ecc.layout = &vf610_nfc_ecc60;
                } else if (chip->ecc.strength == 24) {
                        nfc->ecc_mode = ECC_45_BYTE;
                        chip->ecc.bytes = 45;
-                       chip->ecc.layout = &vf610_nfc_ecc45;
                } else {
                        dev_err(nfc->dev, "Unsupported ECC strength\n");
                        err = -ENXIO;
index af28bb3..a4b029a 100644 (file)
@@ -68,21 +68,33 @@ MODULE_PARM_DESC(otp,       "Corresponding behaviour of OneNAND in OTP"
  * flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page
  * For now, we expose only 64 out of 80 ecc bytes
  */
-static struct nand_ecclayout flexonenand_oob_128 = {
-       .eccbytes       = 64,
-       .eccpos         = {
-               6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-               22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-               38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-               54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-               70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-               86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
-               102, 103, 104, 105
-               },
-       .oobfree        = {
-               {2, 4}, {18, 4}, {34, 4}, {50, 4},
-               {66, 4}, {82, 4}, {98, 4}, {114, 4}
-       }
+static int flexonenand_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                    struct mtd_oob_region *oobregion)
+{
+       if (section > 7)
+               return -ERANGE;
+
+       oobregion->offset = (section * 16) + 6;
+       oobregion->length = 10;
+
+       return 0;
+}
+
+static int flexonenand_ooblayout_free(struct mtd_info *mtd, int section,
+                                     struct mtd_oob_region *oobregion)
+{
+       if (section > 7)
+               return -ERANGE;
+
+       oobregion->offset = (section * 16) + 2;
+       oobregion->length = 4;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops flexonenand_ooblayout_ops = {
+       .ecc = flexonenand_ooblayout_ecc,
+       .free = flexonenand_ooblayout_free,
 };
 
 /*
@@ -91,56 +103,77 @@ static struct nand_ecclayout flexonenand_oob_128 = {
  * Based on specification:
  * 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010
  *
- * For eccpos we expose only 64 bytes out of 72 (see struct nand_ecclayout)
- *
- * oobfree uses the spare area fields marked as
- * "Managed by internal ECC logic for Logical Sector Number area"
  */
-static struct nand_ecclayout onenand_oob_128 = {
-       .eccbytes       = 64,
-       .eccpos         = {
-               7, 8, 9, 10, 11, 12, 13, 14, 15,
-               23, 24, 25, 26, 27, 28, 29, 30, 31,
-               39, 40, 41, 42, 43, 44, 45, 46, 47,
-               55, 56, 57, 58, 59, 60, 61, 62, 63,
-               71, 72, 73, 74, 75, 76, 77, 78, 79,
-               87, 88, 89, 90, 91, 92, 93, 94, 95,
-               103, 104, 105, 106, 107, 108, 109, 110, 111,
-               119
-       },
-       .oobfree        = {
-               {2, 3}, {18, 3}, {34, 3}, {50, 3},
-               {66, 3}, {82, 3}, {98, 3}, {114, 3}
-       }
+static int onenand_ooblayout_128_ecc(struct mtd_info *mtd, int section,
+                                    struct mtd_oob_region *oobregion)
+{
+       if (section > 7)
+               return -ERANGE;
+
+       oobregion->offset = (section * 16) + 7;
+       oobregion->length = 9;
+
+       return 0;
+}
+
+static int onenand_ooblayout_128_free(struct mtd_info *mtd, int section,
+                                     struct mtd_oob_region *oobregion)
+{
+       if (section >= 8)
+               return -ERANGE;
+
+       /*
+        * free bytes are using the spare area fields marked as
+        * "Managed by internal ECC logic for Logical Sector Number area"
+        */
+       oobregion->offset = (section * 16) + 2;
+       oobregion->length = 3;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops onenand_oob_128_ooblayout_ops = {
+       .ecc = onenand_ooblayout_128_ecc,
+       .free = onenand_ooblayout_128_free,
 };
 
 /**
- * onenand_oob_64 - oob info for large (2KB) page
+ * onenand_oob_32_64 - oob info for large (2KB) page
  */
-static struct nand_ecclayout onenand_oob_64 = {
-       .eccbytes       = 20,
-       .eccpos         = {
-               8, 9, 10, 11, 12,
-               24, 25, 26, 27, 28,
-               40, 41, 42, 43, 44,
-               56, 57, 58, 59, 60,
-               },
-       .oobfree        = {
-               {2, 3}, {14, 2}, {18, 3}, {30, 2},
-               {34, 3}, {46, 2}, {50, 3}, {62, 2}
+static int onenand_ooblayout_32_64_ecc(struct mtd_info *mtd, int section,
+                                      struct mtd_oob_region *oobregion)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       oobregion->offset = (section * 16) + 8;
+       oobregion->length = 5;
+
+       return 0;
+}
+
+static int onenand_ooblayout_32_64_free(struct mtd_info *mtd, int section,
+                                       struct mtd_oob_region *oobregion)
+{
+       int sections = (mtd->oobsize / 32) * 2;
+
+       if (section >= sections)
+               return -ERANGE;
+
+       if (section & 1) {
+               oobregion->offset = ((section - 1) * 16) + 14;
+               oobregion->length = 2;
+       } else  {
+               oobregion->offset = (section * 16) + 2;
+               oobregion->length = 3;
        }
-};
 
-/**
- * onenand_oob_32 - oob info for middle (1KB) page
- */
-static struct nand_ecclayout onenand_oob_32 = {
-       .eccbytes       = 10,
-       .eccpos         = {
-               8, 9, 10, 11, 12,
-               24, 25, 26, 27, 28,
-               },
-       .oobfree        = { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops onenand_oob_32_64_ooblayout_ops = {
+       .ecc = onenand_ooblayout_32_64_ecc,
+       .free = onenand_ooblayout_32_64_free,
 };
 
 static const unsigned char ffchars[] = {
@@ -1024,34 +1057,15 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
                                int thislen)
 {
        struct onenand_chip *this = mtd->priv;
-       struct nand_oobfree *free;
-       int readcol = column;
-       int readend = column + thislen;
-       int lastgap = 0;
-       unsigned int i;
-       uint8_t *oob_buf = this->oob_buf;
-
-       free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
-               if (readcol >= lastgap)
-                       readcol += free->offset - lastgap;
-               if (readend >= lastgap)
-                       readend += free->offset - lastgap;
-               lastgap = free->offset + free->length;
-       }
-       this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
-       free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
-               int free_end = free->offset + free->length;
-               if (free->offset < readend && free_end > readcol) {
-                       int st = max_t(int,free->offset,readcol);
-                       int ed = min_t(int,free_end,readend);
-                       int n = ed - st;
-                       memcpy(buf, oob_buf + st, n);
-                       buf += n;
-               } else if (column == 0)
-                       break;
-       }
+       int ret;
+
+       this->read_bufferram(mtd, ONENAND_SPARERAM, this->oob_buf, 0,
+                            mtd->oobsize);
+       ret = mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf,
+                                         column, thislen);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
@@ -1808,34 +1822,7 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
 static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
                                  const u_char *buf, int column, int thislen)
 {
-       struct onenand_chip *this = mtd->priv;
-       struct nand_oobfree *free;
-       int writecol = column;
-       int writeend = column + thislen;
-       int lastgap = 0;
-       unsigned int i;
-
-       free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
-               if (writecol >= lastgap)
-                       writecol += free->offset - lastgap;
-               if (writeend >= lastgap)
-                       writeend += free->offset - lastgap;
-               lastgap = free->offset + free->length;
-       }
-       free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
-               int free_end = free->offset + free->length;
-               if (free->offset < writeend && free_end > writecol) {
-                       int st = max_t(int,free->offset,writecol);
-                       int ed = min_t(int,free_end,writeend);
-                       int n = ed - st;
-                       memcpy(oob_buf + st, buf, n);
-                       buf += n;
-               } else if (column == 0)
-                       break;
-       }
-       return 0;
+       return mtd_ooblayout_set_databytes(mtd, buf, oob_buf, column, thislen);
 }
 
 /**
@@ -4003,22 +3990,22 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        switch (mtd->oobsize) {
        case 128:
                if (FLEXONENAND(this)) {
-                       this->ecclayout = &flexonenand_oob_128;
+                       mtd_set_ooblayout(mtd, &flexonenand_ooblayout_ops);
                        mtd->subpage_sft = 0;
                } else {
-                       this->ecclayout = &onenand_oob_128;
+                       mtd_set_ooblayout(mtd, &onenand_oob_128_ooblayout_ops);
                        mtd->subpage_sft = 2;
                }
                if (ONENAND_IS_NOP_1(this))
                        mtd->subpage_sft = 0;
                break;
        case 64:
-               this->ecclayout = &onenand_oob_64;
+               mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
                mtd->subpage_sft = 2;
                break;
 
        case 32:
-               this->ecclayout = &onenand_oob_32;
+               mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
                mtd->subpage_sft = 1;
                break;
 
@@ -4027,7 +4014,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
                        __func__, mtd->oobsize);
                mtd->subpage_sft = 0;
                /* To prevent kernel oops */
-               this->ecclayout = &onenand_oob_32;
+               mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
                break;
        }
 
@@ -4037,12 +4024,12 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
         * The number of bytes available for a client to place data into
         * the out of band area
         */
-       mtd->oobavail = 0;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
-           this->ecclayout->oobfree[i].length; i++)
-               mtd->oobavail += this->ecclayout->oobfree[i].length;
+       ret = mtd_ooblayout_count_freebytes(mtd);
+       if (ret < 0)
+               ret = 0;
+
+       mtd->oobavail = ret;
 
-       mtd->ecclayout = this->ecclayout;
        mtd->ecc_strength = 1;
 
        /* Fill in remaining MTD driver data */
index 157841d..c52e455 100644 (file)
@@ -832,6 +832,7 @@ static const struct flash_info spi_nor_ids[] = {
        /* GigaDevice */
        { "gd25q32", INFO(0xc84016, 0, 64 * 1024,  64, SECT_4K) },
        { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
+       { "gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
        { "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, SECT_4K) },
 
        /* Intel/Numonyx -- xxxs33b */
index bee3fa9..d7efd9d 100644 (file)
@@ -10,7 +10,6 @@ obj-$(CONFIG_OF_UNITTEST) += unittest.o
 obj-$(CONFIG_OF_MDIO)  += of_mdio.o
 obj-$(CONFIG_OF_PCI)   += of_pci.o
 obj-$(CONFIG_OF_PCI_IRQ)  += of_pci_irq.o
-obj-$(CONFIG_OF_MTD)   += of_mtd.o
 obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
 obj-$(CONFIG_OF_RESOLVE)  += resolver.o
 obj-$(CONFIG_OF_OVERLAY) += overlay.o
diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
deleted file mode 100644 (file)
index b7361ed..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * OF helpers for mtd.
- *
- * This file is released under the GPLv2
- *
- */
-#include <linux/kernel.h>
-#include <linux/of_mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/export.h>
-
-/**
- * It maps 'enum nand_ecc_modes_t' found in include/linux/mtd/nand.h
- * into the device tree binding of 'nand-ecc', so that MTD
- * device driver can get nand ecc from device tree.
- */
-static const char *nand_ecc_modes[] = {
-       [NAND_ECC_NONE]         = "none",
-       [NAND_ECC_SOFT]         = "soft",
-       [NAND_ECC_HW]           = "hw",
-       [NAND_ECC_HW_SYNDROME]  = "hw_syndrome",
-       [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
-       [NAND_ECC_SOFT_BCH]     = "soft_bch",
-};
-
-/**
- * of_get_nand_ecc_mode - Get nand ecc mode for given device_node
- * @np:        Pointer to the given device_node
- *
- * The function gets ecc mode string from property 'nand-ecc-mode',
- * and return its index in nand_ecc_modes table, or errno in error case.
- */
-int of_get_nand_ecc_mode(struct device_node *np)
-{
-       const char *pm;
-       int err, i;
-
-       err = of_property_read_string(np, "nand-ecc-mode", &pm);
-       if (err < 0)
-               return err;
-
-       for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
-               if (!strcasecmp(pm, nand_ecc_modes[i]))
-                       return i;
-
-       return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
-
-/**
- * of_get_nand_ecc_step_size - Get ECC step size associated to
- * the required ECC strength (see below).
- * @np:        Pointer to the given device_node
- *
- * return the ECC step size, or errno in error case.
- */
-int of_get_nand_ecc_step_size(struct device_node *np)
-{
-       int ret;
-       u32 val;
-
-       ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
-       return ret ? ret : val;
-}
-EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size);
-
-/**
- * of_get_nand_ecc_strength - Get required ECC strength over the
- * correspnding step size as defined by 'nand-ecc-size'
- * @np:        Pointer to the given device_node
- *
- * return the ECC strength, or errno in error case.
- */
-int of_get_nand_ecc_strength(struct device_node *np)
-{
-       int ret;
-       u32 val;
-
-       ret = of_property_read_u32(np, "nand-ecc-strength", &val);
-       return ret ? ret : val;
-}
-EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength);
-
-/**
- * of_get_nand_bus_width - Get nand bus witdh for given device_node
- * @np:        Pointer to the given device_node
- *
- * return bus width option, or errno in error case.
- */
-int of_get_nand_bus_width(struct device_node *np)
-{
-       u32 val;
-
-       if (of_property_read_u32(np, "nand-bus-width", &val))
-               return 8;
-
-       switch(val) {
-       case 8:
-       case 16:
-               return val;
-       default:
-               return -EIO;
-       }
-}
-EXPORT_SYMBOL_GPL(of_get_nand_bus_width);
-
-/**
- * of_get_nand_on_flash_bbt - Get nand on flash bbt for given device_node
- * @np:        Pointer to the given device_node
- *
- * return true if present false other wise
- */
-bool of_get_nand_on_flash_bbt(struct device_node *np)
-{
-       return of_property_read_bool(np, "nand-on-flash-bbt");
-}
-EXPORT_SYMBOL_GPL(of_get_nand_on_flash_bbt);
index 163f21a..e389009 100644 (file)
@@ -42,23 +42,33 @@ static inline struct spinand_state *mtd_to_state(struct mtd_info *mtd)
 static int enable_hw_ecc;
 static int enable_read_hw_ecc;
 
-static struct nand_ecclayout spinand_oob_64 = {
-       .eccbytes = 24,
-       .eccpos = {
-               1, 2, 3, 4, 5, 6,
-               17, 18, 19, 20, 21, 22,
-               33, 34, 35, 36, 37, 38,
-               49, 50, 51, 52, 53, 54, },
-       .oobfree = {
-               {.offset = 8,
-                       .length = 8},
-               {.offset = 24,
-                       .length = 8},
-               {.offset = 40,
-                       .length = 8},
-               {.offset = 56,
-                       .length = 8},
-       }
+static int spinand_ooblayout_64_ecc(struct mtd_info *mtd, int section,
+                                   struct mtd_oob_region *oobregion)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       oobregion->offset = (section * 16) + 1;
+       oobregion->length = 6;
+
+       return 0;
+}
+
+static int spinand_ooblayout_64_free(struct mtd_info *mtd, int section,
+                                    struct mtd_oob_region *oobregion)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       oobregion->offset = (section * 16) + 8;
+       oobregion->length = 8;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops spinand_oob_64_ops = {
+       .ecc = spinand_ooblayout_64_ecc,
+       .free = spinand_ooblayout_64_free,
 };
 #endif
 
@@ -886,11 +896,11 @@ static int spinand_probe(struct spi_device *spi_nand)
 
        chip->ecc.strength = 1;
        chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
-       chip->ecc.layout = &spinand_oob_64;
        chip->ecc.read_page = spinand_read_page_hwecc;
        chip->ecc.write_page = spinand_write_page_hwecc;
 #else
        chip->ecc.mode  = NAND_ECC_SOFT;
+       chip->ecc.algo  = NAND_ECC_HAMMING;
        if (spinand_disable_ecc(spi_nand) < 0)
                dev_info(&spi_nand->dev, "%s: disable ecc failed!\n",
                         __func__);
@@ -912,6 +922,9 @@ static int spinand_probe(struct spi_device *spi_nand)
 
        mtd->dev.parent = &spi_nand->dev;
        mtd->oobsize = 64;
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+       mtd_set_ooblayout(mtd, &spinand_oob_64_ops);
+#endif
 
        if (nand_scan(mtd, 1))
                return -ENXIO;
index 846513c..a5ac2ca 100644 (file)
@@ -587,7 +587,6 @@ struct mtd_info;
 
 struct bcma_sflash {
        bool present;
-       u32 window;
        u32 blocksize;
        u16 numblocks;
        u32 size;
index 0023088..3f9778c 100644 (file)
 #define FSL_IFC_VERSION_MASK   0x0F0F0000
 #define FSL_IFC_VERSION_1_0_0  0x01000000
 #define FSL_IFC_VERSION_1_1_0  0x01010000
+#define FSL_IFC_VERSION_2_0_0  0x02000000
+
+#define PGOFFSET_64K   (64*1024)
+#define PGOFFSET_4K    (4*1024)
 
 /*
  * CSPR - Chip Select Property Register
@@ -723,20 +727,26 @@ struct fsl_ifc_nand {
        __be32 nand_evter_en;
        u32 res17[0x2];
        __be32 nand_evter_intr_en;
-       u32 res18[0x2];
+       __be32 nand_vol_addr_stat;
+       u32 res18;
        __be32 nand_erattr0;
        __be32 nand_erattr1;
        u32 res19[0x10];
        __be32 nand_fsr;
-       u32 res20;
-       __be32 nand_eccstat[4];
-       u32 res21[0x20];
+       u32 res20[0x3];
+       __be32 nand_eccstat[6];
+       u32 res21[0x1c];
        __be32 nanndcr;
        u32 res22[0x2];
        __be32 nand_autoboot_trgr;
        u32 res23;
        __be32 nand_mdr;
-       u32 res24[0x5C];
+       u32 res24[0x1C];
+       __be32 nand_dll_lowcfg0;
+       __be32 nand_dll_lowcfg1;
+       u32 res25;
+       __be32 nand_dll_lowstat;
+       u32 res26[0x3c];
 };
 
 /*
@@ -771,13 +781,12 @@ struct fsl_ifc_gpcm {
        __be32 gpcm_erattr1;
        __be32 gpcm_erattr2;
        __be32 gpcm_stat;
-       u32 res4[0x1F3];
 };
 
 /*
  * IFC Controller Registers
  */
-struct fsl_ifc_regs {
+struct fsl_ifc_global {
        __be32 ifc_rev;
        u32 res1[0x2];
        struct {
@@ -803,21 +812,26 @@ struct fsl_ifc_regs {
        } ftim_cs[FSL_IFC_BANK_COUNT];
        u32 res9[0x30];
        __be32 rb_stat;
-       u32 res10[0x2];
+       __be32 rb_map;
+       __be32 wb_map;
        __be32 ifc_gcr;
-       u32 res11[0x2];
+       u32 res10[0x2];
        __be32 cm_evter_stat;
-       u32 res12[0x2];
+       u32 res11[0x2];
        __be32 cm_evter_en;
-       u32 res13[0x2];
+       u32 res12[0x2];
        __be32 cm_evter_intr_en;
-       u32 res14[0x2];
+       u32 res13[0x2];
        __be32 cm_erattr0;
        __be32 cm_erattr1;
-       u32 res15[0x2];
+       u32 res14[0x2];
        __be32 ifc_ccr;
        __be32 ifc_csr;
-       u32 res16[0x2EB];
+       __be32 ddr_ccr_low;
+};
+
+
+struct fsl_ifc_runtime {
        struct fsl_ifc_nand ifc_nand;
        struct fsl_ifc_nor ifc_nor;
        struct fsl_ifc_gpcm ifc_gpcm;
@@ -831,7 +845,8 @@ extern int fsl_ifc_find(phys_addr_t addr_base);
 struct fsl_ifc_ctrl {
        /* device info */
        struct device                   *dev;
-       struct fsl_ifc_regs __iomem     *regs;
+       struct fsl_ifc_global __iomem   *gregs;
+       struct fsl_ifc_runtime __iomem  *rregs;
        int                             irq;
        int                             nand_irq;
        spinlock_t                      lock;
index c8be32e..ad3c348 100644 (file)
 
 #define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
 
-/*
- * There are 13 bytes of ecc for every 512 byte block in FSMC version 8
- * and it has to be read consecutively and immediately after the 512
- * byte data block for hardware to generate the error bit offsets
- * Managing the ecc bytes in the following way is easier. This way is
- * similar to oobfree structure maintained already in u-boot nand driver
- */
-#define MAX_ECCPLACE_ENTRIES   32
-
-struct fsmc_nand_eccplace {
-       uint8_t offset;
-       uint8_t length;
-};
-
-struct fsmc_eccplace {
-       struct fsmc_nand_eccplace eccplace[MAX_ECCPLACE_ENTRIES];
-};
-
 struct fsmc_nand_timings {
        uint8_t tclr;
        uint8_t tar;
index 5e0eb7c..3aa56e3 100644 (file)
 #endif
 
 #ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
-# ifdef map_bankwidth
-#  undef map_bankwidth
-#  define map_bankwidth(map) ((map)->bankwidth)
-#  undef map_bankwidth_is_large
-#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
-#  undef map_words
-#  define map_words(map) map_calc_words(map)
-# else
-#  define map_bankwidth(map) 32
-#  define map_bankwidth_is_large(map) (1)
-#  define map_words(map) map_calc_words(map)
-# endif
+/* always use indirect access for 256-bit to preserve kernel stack */
+# undef map_bankwidth
+# define map_bankwidth(map) ((map)->bankwidth)
+# undef map_bankwidth_is_large
+# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+# undef map_words
+# define map_words(map) map_calc_words(map)
 #define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
 #undef MAX_MAP_BANKWIDTH
 #define MAX_MAP_BANKWIDTH 32
index ef9fea4..29a1706 100644 (file)
@@ -96,16 +96,35 @@ struct mtd_oob_ops {
 
 #define MTD_MAX_OOBFREE_ENTRIES_LARGE  32
 #define MTD_MAX_ECCPOS_ENTRIES_LARGE   640
+/**
+ * struct mtd_oob_region - oob region definition
+ * @offset: region offset
+ * @length: region length
+ *
+ * This structure describes a region of the OOB area, and is used
+ * to retrieve ECC or free bytes sections.
+ * Each section is defined by an offset within the OOB area and a
+ * length.
+ */
+struct mtd_oob_region {
+       u32 offset;
+       u32 length;
+};
+
 /*
- * Internal ECC layout control structure. For historical reasons, there is a
- * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
- * for export to user-space via the ECCGETLAYOUT ioctl.
- * nand_ecclayout should be expandable in the future simply by the above macros.
+ * struct mtd_ooblayout_ops - NAND OOB layout operations
+ * @ecc: function returning an ECC region in the OOB area.
+ *      Should return -ERANGE if %section exceeds the total number of
+ *      ECC sections.
+ * @free: function returning a free region in the OOB area.
+ *       Should return -ERANGE if %section exceeds the total number of
+ *       free sections.
  */
-struct nand_ecclayout {
-       __u32 eccbytes;
-       __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
-       struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE];
+struct mtd_ooblayout_ops {
+       int (*ecc)(struct mtd_info *mtd, int section,
+                  struct mtd_oob_region *oobecc);
+       int (*free)(struct mtd_info *mtd, int section,
+                   struct mtd_oob_region *oobfree);
 };
 
 struct module; /* only needed for owner field in mtd_info */
@@ -166,8 +185,8 @@ struct mtd_info {
        const char *name;
        int index;
 
-       /* ECC layout structure pointer - read only! */
-       struct nand_ecclayout *ecclayout;
+       /* OOB layout description */
+       const struct mtd_ooblayout_ops *ooblayout;
 
        /* the ecc step size. */
        unsigned int ecc_step_size;
@@ -253,6 +272,30 @@ struct mtd_info {
        int usecount;
 };
 
+int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
+                     struct mtd_oob_region *oobecc);
+int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
+                                int *section,
+                                struct mtd_oob_region *oobregion);
+int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
+                              const u8 *oobbuf, int start, int nbytes);
+int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
+                              u8 *oobbuf, int start, int nbytes);
+int mtd_ooblayout_free(struct mtd_info *mtd, int section,
+                      struct mtd_oob_region *oobfree);
+int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
+                               const u8 *oobbuf, int start, int nbytes);
+int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
+                               u8 *oobbuf, int start, int nbytes);
+int mtd_ooblayout_count_freebytes(struct mtd_info *mtd);
+int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd);
+
+static inline void mtd_set_ooblayout(struct mtd_info *mtd,
+                                    const struct mtd_ooblayout_ops *ooblayout)
+{
+       mtd->ooblayout = ooblayout;
+}
+
 static inline void mtd_set_of_node(struct mtd_info *mtd,
                                   struct device_node *np)
 {
index 56574ba..fbe8e16 100644 (file)
@@ -116,9 +116,14 @@ typedef enum {
        NAND_ECC_HW,
        NAND_ECC_HW_SYNDROME,
        NAND_ECC_HW_OOB_FIRST,
-       NAND_ECC_SOFT_BCH,
 } nand_ecc_modes_t;
 
+enum nand_ecc_algo {
+       NAND_ECC_UNKNOWN,
+       NAND_ECC_HAMMING,
+       NAND_ECC_BCH,
+};
+
 /*
  * Constants for Hardware ECC
  */
@@ -458,6 +463,7 @@ struct nand_hw_control {
 /**
  * struct nand_ecc_ctrl - Control structure for ECC
  * @mode:      ECC mode
+ * @algo:      ECC algorithm
  * @steps:     number of ECC steps per page
  * @size:      data bytes per ECC step
  * @bytes:     ECC bytes per step
@@ -466,7 +472,6 @@ struct nand_hw_control {
  * @prepad:    padding information for syndrome based ECC generators
  * @postpad:   padding information for syndrome based ECC generators
  * @options:   ECC specific options (see NAND_ECC_XXX flags defined above)
- * @layout:    ECC layout control struct pointer
  * @priv:      pointer to private ECC control data
  * @hwctl:     function to control hardware ECC generator. Must only
  *             be provided if an hardware ECC is available
@@ -508,6 +513,7 @@ struct nand_hw_control {
  */
 struct nand_ecc_ctrl {
        nand_ecc_modes_t mode;
+       enum nand_ecc_algo algo;
        int steps;
        int size;
        int bytes;
@@ -516,7 +522,6 @@ struct nand_ecc_ctrl {
        int prepad;
        int postpad;
        unsigned int options;
-       struct nand_ecclayout   *layout;
        void *priv;
        void (*hwctl)(struct mtd_info *mtd, int mode);
        int (*calculate)(struct mtd_info *mtd, const uint8_t *dat,
@@ -740,6 +745,9 @@ struct nand_chip {
        void *priv;
 };
 
+extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;
+extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops;
+
 static inline void nand_set_flash_node(struct nand_chip *chip,
                                       struct device_node *np)
 {
@@ -1070,4 +1078,18 @@ int nand_check_erased_ecc_chunk(void *data, int datalen,
                                void *ecc, int ecclen,
                                void *extraoob, int extraooblen,
                                int threshold);
+
+/* Default write_oob implementation */
+int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);
+
+/* Default write_oob syndrome implementation */
+int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+                           int page);
+
+/* Default read_oob implementation */
+int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);
+
+/* Default read_oob syndrome implementation */
+int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+                          int page);
 #endif /* __LINUX_MTD_NAND_H */
index 4596503..0aaa98b 100644 (file)
@@ -80,7 +80,6 @@ struct onenand_bufferram {
  * @page_buf:          [INTERN] page main data buffer
  * @oob_buf:           [INTERN] page oob data buffer
  * @subpagesize:       [INTERN] holds the subpagesize
- * @ecclayout:         [REPLACEABLE] the default ecc placement scheme
  * @bbm:               [REPLACEABLE] pointer to Bad Block Management
  * @priv:              [OPTIONAL] pointer to private chip date
  */
@@ -134,7 +133,6 @@ struct onenand_chip {
 #endif
 
        int                     subpagesize;
-       struct nand_ecclayout   *ecclayout;
 
        void                    *bbm;
 
index 25f4d2a..65e91d0 100644 (file)
@@ -14,7 +14,7 @@
 
 struct sharpsl_nand_platform_data {
        struct nand_bbt_descr   *badblock_pattern;
-       struct nand_ecclayout   *ecc_layout;
+       const struct mtd_ooblayout_ops *ecc_layout;
        struct mtd_partition    *partitions;
        unsigned int            nr_partitions;
 };
index 3c36113..7f041bd 100644 (file)
@@ -21,6 +21,7 @@
  * Sometimes these are the same as CFI IDs, but sometimes they aren't.
  */
 #define SNOR_MFR_ATMEL         CFI_MFR_ATMEL
+#define SNOR_MFR_GIGADEVICE    0xc8
 #define SNOR_MFR_INTEL         CFI_MFR_INTEL
 #define SNOR_MFR_MICRON                CFI_MFR_ST /* ST Micro <--> Micron */
 #define SNOR_MFR_MACRONIX      CFI_MFR_MACRONIX
diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h
deleted file mode 100644 (file)
index e266caa..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * OF helpers for mtd.
- *
- * This file is released under the GPLv2
- */
-
-#ifndef __LINUX_OF_MTD_H
-#define __LINUX_OF_MTD_H
-
-#ifdef CONFIG_OF_MTD
-
-#include <linux/of.h>
-int of_get_nand_ecc_mode(struct device_node *np);
-int of_get_nand_ecc_step_size(struct device_node *np);
-int of_get_nand_ecc_strength(struct device_node *np);
-int of_get_nand_bus_width(struct device_node *np);
-bool of_get_nand_on_flash_bbt(struct device_node *np);
-
-#else /* CONFIG_OF_MTD */
-
-static inline int of_get_nand_ecc_mode(struct device_node *np)
-{
-       return -ENOSYS;
-}
-
-static inline int of_get_nand_ecc_step_size(struct device_node *np)
-{
-       return -ENOSYS;
-}
-
-static inline int of_get_nand_ecc_strength(struct device_node *np)
-{
-       return -ENOSYS;
-}
-
-static inline int of_get_nand_bus_width(struct device_node *np)
-{
-       return -ENOSYS;
-}
-
-static inline bool of_get_nand_on_flash_bbt(struct device_node *np)
-{
-       return false;
-}
-
-#endif /* CONFIG_OF_MTD */
-
-#endif /* __LINUX_OF_MTD_H */
index d833eb4..9e9d79e 100644 (file)
  *  option) any later version.
  */
 
-/* Maximum Number of Chip Selects */
-#define GPMC_CS_NUM            8
+#include <linux/platform_data/gpmc-omap.h>
 
 #define GPMC_CONFIG_WP         0x00000005
 
-#define GPMC_IRQ_FIFOEVENTENABLE       0x01
-#define GPMC_IRQ_COUNT_EVENT           0x02
-
-#define GPMC_BURST_4                   4       /* 4 word burst */
-#define GPMC_BURST_8                   8       /* 8 word burst */
-#define GPMC_BURST_16                  16      /* 16 word burst */
-#define GPMC_DEVWIDTH_8BIT             1       /* 8-bit device width */
-#define GPMC_DEVWIDTH_16BIT            2       /* 16-bit device width */
-#define GPMC_MUX_AAD                   1       /* Addr-Addr-Data multiplex */
-#define GPMC_MUX_AD                    2       /* Addr-Data multiplex */
-
-/* bool type time settings */
-struct gpmc_bool_timings {
-       bool cycle2cyclediffcsen;
-       bool cycle2cyclesamecsen;
-       bool we_extra_delay;
-       bool oe_extra_delay;
-       bool adv_extra_delay;
-       bool cs_extra_delay;
-       bool time_para_granularity;
-};
+/* IRQ numbers in GPMC IRQ domain for legacy boot use */
+#define GPMC_IRQ_FIFOEVENTENABLE       0
+#define GPMC_IRQ_COUNT_EVENT           1
 
-/*
- * Note that all values in this struct are in nanoseconds except sync_clk
- * (which is in picoseconds), while the register values are in gpmc_fck cycles.
+/**
+ * gpmc_nand_ops - Interface between NAND and GPMC
+ * @nand_write_buffer_empty: get the NAND write buffer empty status.
  */
-struct gpmc_timings {
-       /* Minimum clock period for synchronous mode (in picoseconds) */
-       u32 sync_clk;
-
-       /* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */
-       u32 cs_on;              /* Assertion time */
-       u32 cs_rd_off;          /* Read deassertion time */
-       u32 cs_wr_off;          /* Write deassertion time */
-
-       /* ADV signal timings corresponding to GPMC_CONFIG3 */
-       u32 adv_on;             /* Assertion time */
-       u32 adv_rd_off;         /* Read deassertion time */
-       u32 adv_wr_off;         /* Write deassertion time */
-       u32 adv_aad_mux_on;     /* ADV assertion time for AAD */
-       u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */
-       u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */
-
-       /* WE signals timings corresponding to GPMC_CONFIG4 */
-       u32 we_on;              /* WE assertion time */
-       u32 we_off;             /* WE deassertion time */
-
-       /* OE signals timings corresponding to GPMC_CONFIG4 */
-       u32 oe_on;              /* OE assertion time */
-       u32 oe_off;             /* OE deassertion time */
-       u32 oe_aad_mux_on;      /* OE assertion time for AAD */
-       u32 oe_aad_mux_off;     /* OE deassertion time for AAD */
-
-       /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */
-       u32 page_burst_access;  /* Multiple access word delay */
-       u32 access;             /* Start-cycle to first data valid delay */
-       u32 rd_cycle;           /* Total read cycle time */
-       u32 wr_cycle;           /* Total write cycle time */
-
-       u32 bus_turnaround;
-       u32 cycle2cycle_delay;
-
-       u32 wait_monitoring;
-       u32 clk_activation;
-
-       /* The following are only on OMAP3430 */
-       u32 wr_access;          /* WRACCESSTIME */
-       u32 wr_data_mux_bus;    /* WRDATAONADMUXBUS */
-
-       struct gpmc_bool_timings bool_timings;
+struct gpmc_nand_ops {
+       bool (*nand_writebuffer_empty)(void);
 };
 
-/* Device timings in picoseconds */
-struct gpmc_device_timings {
-       u32 t_ceasu;    /* address setup to CS valid */
-       u32 t_avdasu;   /* address setup to ADV valid */
-       /* XXX: try to combine t_avdp_r & t_avdp_w. Issue is
-        * of tusb using these timings even for sync whilst
-        * ideally for adv_rd/(wr)_off it should have considered
-        * t_avdh instead. This indirectly necessitates r/w
-        * variations of t_avdp as it is possible to have one
-        * sync & other async
-        */
-       u32 t_avdp_r;   /* ADV low time (what about t_cer ?) */
-       u32 t_avdp_w;
-       u32 t_aavdh;    /* address hold time */
-       u32 t_oeasu;    /* address setup to OE valid */
-       u32 t_aa;       /* access time from ADV assertion */
-       u32 t_iaa;      /* initial access time */
-       u32 t_oe;       /* access time from OE assertion */
-       u32 t_ce;       /* access time from CS asertion */
-       u32 t_rd_cycle; /* read cycle time */
-       u32 t_cez_r;    /* read CS deassertion to high Z */
-       u32 t_cez_w;    /* write CS deassertion to high Z */
-       u32 t_oez;      /* OE deassertion to high Z */
-       u32 t_weasu;    /* address setup to WE valid */
-       u32 t_wpl;      /* write assertion time */
-       u32 t_wph;      /* write deassertion time */
-       u32 t_wr_cycle; /* write cycle time */
-
-       u32 clk;
-       u32 t_bacc;     /* burst access valid clock to output delay */
-       u32 t_ces;      /* CS setup time to clk */
-       u32 t_avds;     /* ADV setup time to clk */
-       u32 t_avdh;     /* ADV hold time from clk */
-       u32 t_ach;      /* address hold time from clk */
-       u32 t_rdyo;     /* clk to ready valid */
-
-       u32 t_ce_rdyz;  /* XXX: description ?, or use t_cez instead */
-       u32 t_ce_avd;   /* CS on to ADV on delay */
-
-       /* XXX: check the possibility of combining
-        * cyc_aavhd_oe & cyc_aavdh_we
-        */
-       u8 cyc_aavdh_oe;/* read address hold time in cycles */
-       u8 cyc_aavdh_we;/* write address hold time in cycles */
-       u8 cyc_oe;      /* access time from OE assertion in cycles */
-       u8 cyc_wpl;     /* write deassertion time in cycles */
-       u32 cyc_iaa;    /* initial access time in cycles */
-
-       /* extra delays */
-       bool ce_xdelay;
-       bool avd_xdelay;
-       bool oe_xdelay;
-       bool we_xdelay;
-};
+struct gpmc_nand_regs;
 
-struct gpmc_settings {
-       bool burst_wrap;        /* enables wrap bursting */
-       bool burst_read;        /* enables read page/burst mode */
-       bool burst_write;       /* enables write page/burst mode */
-       bool device_nand;       /* device is NAND */
-       bool sync_read;         /* enables synchronous reads */
-       bool sync_write;        /* enables synchronous writes */
-       bool wait_on_read;      /* monitor wait on reads */
-       bool wait_on_write;     /* monitor wait on writes */
-       u32 burst_len;          /* page/burst length */
-       u32 device_width;       /* device bus width (8 or 16 bit) */
-       u32 mux_add_data;       /* multiplex address & data */
-       u32 wait_pin;           /* wait-pin to be used */
-};
+#if IS_ENABLED(CONFIG_OMAP_GPMC)
+struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
+                                            int cs);
+#else
+static inline gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
+                                                   int cs)
+{
+       return NULL;
+}
+#endif /* CONFIG_OMAP_GPMC */
+
+/*--------------------------------*/
+
+/* deprecated APIs */
+#if IS_ENABLED(CONFIG_OMAP_GPMC)
+void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs);
+#else
+static inline void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
+{
+}
+#endif /* CONFIG_OMAP_GPMC */
+/*--------------------------------*/
 
 extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t,
                             struct gpmc_settings *gpmc_s,
                             struct gpmc_device_timings *dev_t);
 
-struct gpmc_nand_regs;
 struct device_node;
 
-extern void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs);
 extern int gpmc_get_client_irq(unsigned irq_config);
 
 extern unsigned int gpmc_ticks_to_ns(unsigned int ticks);
diff --git a/include/linux/platform_data/gpmc-omap.h b/include/linux/platform_data/gpmc-omap.h
new file mode 100644 (file)
index 0000000..67ccdb0
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * OMAP GPMC Platform data
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc. - http://www.ti.com
+ *     Roger Quadros <rogerq@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#ifndef _GPMC_OMAP_H_
+#define _GPMC_OMAP_H_
+
+/* Maximum Number of Chip Selects */
+#define GPMC_CS_NUM            8
+
+/* bool type time settings */
+struct gpmc_bool_timings {
+       bool cycle2cyclediffcsen;
+       bool cycle2cyclesamecsen;
+       bool we_extra_delay;
+       bool oe_extra_delay;
+       bool adv_extra_delay;
+       bool cs_extra_delay;
+       bool time_para_granularity;
+};
+
+/*
+ * Note that all values in this struct are in nanoseconds except sync_clk
+ * (which is in picoseconds), while the register values are in gpmc_fck cycles.
+ */
+struct gpmc_timings {
+       /* Minimum clock period for synchronous mode (in picoseconds) */
+       u32 sync_clk;
+
+       /* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */
+       u32 cs_on;              /* Assertion time */
+       u32 cs_rd_off;          /* Read deassertion time */
+       u32 cs_wr_off;          /* Write deassertion time */
+
+       /* ADV signal timings corresponding to GPMC_CONFIG3 */
+       u32 adv_on;             /* Assertion time */
+       u32 adv_rd_off;         /* Read deassertion time */
+       u32 adv_wr_off;         /* Write deassertion time */
+       u32 adv_aad_mux_on;     /* ADV assertion time for AAD */
+       u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */
+       u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */
+
+       /* WE signals timings corresponding to GPMC_CONFIG4 */
+       u32 we_on;              /* WE assertion time */
+       u32 we_off;             /* WE deassertion time */
+
+       /* OE signals timings corresponding to GPMC_CONFIG4 */
+       u32 oe_on;              /* OE assertion time */
+       u32 oe_off;             /* OE deassertion time */
+       u32 oe_aad_mux_on;      /* OE assertion time for AAD */
+       u32 oe_aad_mux_off;     /* OE deassertion time for AAD */
+
+       /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */
+       u32 page_burst_access;  /* Multiple access word delay */
+       u32 access;             /* Start-cycle to first data valid delay */
+       u32 rd_cycle;           /* Total read cycle time */
+       u32 wr_cycle;           /* Total write cycle time */
+
+       u32 bus_turnaround;
+       u32 cycle2cycle_delay;
+
+       u32 wait_monitoring;
+       u32 clk_activation;
+
+       /* The following are only on OMAP3430 */
+       u32 wr_access;          /* WRACCESSTIME */
+       u32 wr_data_mux_bus;    /* WRDATAONADMUXBUS */
+
+       struct gpmc_bool_timings bool_timings;
+};
+
+/* Device timings in picoseconds */
+struct gpmc_device_timings {
+       u32 t_ceasu;    /* address setup to CS valid */
+       u32 t_avdasu;   /* address setup to ADV valid */
+       /* XXX: try to combine t_avdp_r & t_avdp_w. Issue is
+        * of tusb using these timings even for sync whilst
+        * ideally for adv_rd/(wr)_off it should have considered
+        * t_avdh instead. This indirectly necessitates r/w
+        * variations of t_avdp as it is possible to have one
+        * sync & other async
+        */
+       u32 t_avdp_r;   /* ADV low time (what about t_cer ?) */
+       u32 t_avdp_w;
+       u32 t_aavdh;    /* address hold time */
+       u32 t_oeasu;    /* address setup to OE valid */
+       u32 t_aa;       /* access time from ADV assertion */
+       u32 t_iaa;      /* initial access time */
+       u32 t_oe;       /* access time from OE assertion */
+       u32 t_ce;       /* access time from CS asertion */
+       u32 t_rd_cycle; /* read cycle time */
+       u32 t_cez_r;    /* read CS deassertion to high Z */
+       u32 t_cez_w;    /* write CS deassertion to high Z */
+       u32 t_oez;      /* OE deassertion to high Z */
+       u32 t_weasu;    /* address setup to WE valid */
+       u32 t_wpl;      /* write assertion time */
+       u32 t_wph;      /* write deassertion time */
+       u32 t_wr_cycle; /* write cycle time */
+
+       u32 clk;
+       u32 t_bacc;     /* burst access valid clock to output delay */
+       u32 t_ces;      /* CS setup time to clk */
+       u32 t_avds;     /* ADV setup time to clk */
+       u32 t_avdh;     /* ADV hold time from clk */
+       u32 t_ach;      /* address hold time from clk */
+       u32 t_rdyo;     /* clk to ready valid */
+
+       u32 t_ce_rdyz;  /* XXX: description ?, or use t_cez instead */
+       u32 t_ce_avd;   /* CS on to ADV on delay */
+
+       /* XXX: check the possibility of combining
+        * cyc_aavhd_oe & cyc_aavdh_we
+        */
+       u8 cyc_aavdh_oe;/* read address hold time in cycles */
+       u8 cyc_aavdh_we;/* write address hold time in cycles */
+       u8 cyc_oe;      /* access time from OE assertion in cycles */
+       u8 cyc_wpl;     /* write deassertion time in cycles */
+       u32 cyc_iaa;    /* initial access time in cycles */
+
+       /* extra delays */
+       bool ce_xdelay;
+       bool avd_xdelay;
+       bool oe_xdelay;
+       bool we_xdelay;
+};
+
+#define GPMC_BURST_4                   4       /* 4 word burst */
+#define GPMC_BURST_8                   8       /* 8 word burst */
+#define GPMC_BURST_16                  16      /* 16 word burst */
+#define GPMC_DEVWIDTH_8BIT             1       /* 8-bit device width */
+#define GPMC_DEVWIDTH_16BIT            2       /* 16-bit device width */
+#define GPMC_MUX_AAD                   1       /* Addr-Addr-Data multiplex */
+#define GPMC_MUX_AD                    2       /* Addr-Data multiplex */
+
+struct gpmc_settings {
+       bool burst_wrap;        /* enables wrap bursting */
+       bool burst_read;        /* enables read page/burst mode */
+       bool burst_write;       /* enables write page/burst mode */
+       bool device_nand;       /* device is NAND */
+       bool sync_read;         /* enables synchronous reads */
+       bool sync_write;        /* enables synchronous writes */
+       bool wait_on_read;      /* monitor wait on reads */
+       bool wait_on_write;     /* monitor wait on writes */
+       u32 burst_len;          /* page/burst length */
+       u32 device_width;       /* device bus width (8 or 16 bit) */
+       u32 mux_add_data;       /* multiplex address & data */
+       u32 wait_pin;           /* wait-pin to be used */
+};
+
+/* Data for each chip select */
+struct gpmc_omap_cs_data {
+       bool valid;                     /* data is valid */
+       bool is_nand;                   /* device within this CS is NAND */
+       struct gpmc_settings *settings;
+       struct gpmc_device_timings *device_timings;
+       struct gpmc_timings *gpmc_timings;
+       struct platform_device *pdev;   /* device within this CS region */
+       unsigned int pdata_size;
+};
+
+struct gpmc_omap_platform_data {
+       struct gpmc_omap_cs_data cs[GPMC_CS_NUM];
+};
+
+#endif /* _GPMC_OMAP_H */
index 090bbab..17d57a1 100644 (file)
@@ -45,7 +45,6 @@ enum omap_ecc {
 };
 
 struct gpmc_nand_regs {
-       void __iomem    *gpmc_status;
        void __iomem    *gpmc_nand_command;
        void __iomem    *gpmc_nand_address;
        void __iomem    *gpmc_nand_data;
@@ -64,21 +63,24 @@ struct gpmc_nand_regs {
        void __iomem    *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER];
        void __iomem    *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER];
        void __iomem    *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER];
+       /* Deprecated. Do not use */
+       void __iomem    *gpmc_status;
 };
 
 struct omap_nand_platform_data {
        int                     cs;
        struct mtd_partition    *parts;
        int                     nr_parts;
-       bool                    dev_ready;
        bool                    flash_bbt;
        enum nand_io            xfer_type;
        int                     devsize;
        enum omap_ecc           ecc_opt;
-       struct gpmc_nand_regs   reg;
 
-       /* for passing the partitions */
-       struct device_node      *of_node;
        struct device_node      *elm_of_node;
+
+       /* deprecated */
+       struct gpmc_nand_regs   reg;
+       struct device_node      *of_node;
+       bool                    dev_ready;
 };
 #endif
index 763bb69..0ec1da2 100644 (file)
@@ -228,7 +228,7 @@ struct nand_oobfree {
  * complete set of ECC information. The ioctl truncates the larger internal
  * structure to retain binary compatibility with the static declaration of the
  * ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of
- * the user struct, not the MAX size of the internal struct nand_ecclayout.
+ * the user struct, not the MAX size of the internal OOB layout representation.
  */
 struct nand_ecclayout_user {
        __u32 eccbytes;