Merge branch 'akpm' (patches from Andrew)
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 13 Jul 2017 19:38:49 +0000 (12:38 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 13 Jul 2017 19:38:49 +0000 (12:38 -0700)
Merge yet more updates from Andrew Morton:

- various misc things

- kexec updates

- sysctl core updates

- scripts/gdb udpates

- checkpoint-restart updates

- ipc updates

- kernel/watchdog updates

- Kees's "rough equivalent to the glibc _FORTIFY_SOURCE=1 feature"

- "stackprotector: ascii armor the stack canary"

- more MM bits

- checkpatch updates

* emailed patches from Andrew Morton <akpm@linux-foundation.org>: (96 commits)
  writeback: rework wb_[dec|inc]_stat family of functions
  ARM: samsung: usb-ohci: move inline before return type
  video: fbdev: omap: move inline before return type
  video: fbdev: intelfb: move inline before return type
  USB: serial: safe_serial: move __inline__ before return type
  drivers: tty: serial: move inline before return type
  drivers: s390: move static and inline before return type
  x86/efi: move asmlinkage before return type
  sh: move inline before return type
  MIPS: SMP: move asmlinkage before return type
  m68k: coldfire: move inline before return type
  ia64: sn: pci: move inline before type
  ia64: move inline before return type
  FRV: tlbflush: move asmlinkage before return type
  CRIS: gpio: move inline before return type
  ARM: HP Jornada 7XX: move inline before return type
  ARM: KVM: move asmlinkage before type
  checkpatch: improve the STORAGE_CLASS test
  mm, migration: do not trigger OOM killer when migrating memory
  drm/i915: use __GFP_RETRY_MAYFAIL
  ...

231 files changed:
Documentation/ABI/testing/sysfs-class-mtd
Documentation/devicetree/bindings/mtd/denali-nand.txt
Documentation/devicetree/bindings/mtd/elm.txt
Documentation/devicetree/bindings/mtd/gpmc-nand.txt
Documentation/devicetree/bindings/mtd/gpmc-nor.txt
Documentation/devicetree/bindings/mtd/gpmc-onenand.txt
Documentation/devicetree/bindings/mtd/gpmi-nand.txt
Documentation/devicetree/bindings/mtd/microchip,mchp23k256.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mtd/mtk-nand.txt
Documentation/devicetree/bindings/mtd/nand.txt
Documentation/devicetree/bindings/mtd/partition.txt
Documentation/devicetree/bindings/net/gpmc-eth.txt
Documentation/devicetree/bindings/pwm/pwm-meson.txt
Documentation/devicetree/bindings/pwm/pwm-stm32.txt
Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt
Documentation/devicetree/bindings/rtc/brcm,brcmstb-waketimer.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/cortina,gemini.txt [deleted file]
Documentation/devicetree/bindings/rtc/faraday,ftrtc010.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
Documentation/networking/timestamping.txt
Documentation/pwm.txt
Documentation/rtc.txt
MAINTAINERS
Makefile
drivers/gpu/drm/bridge/panel.c
drivers/gpu/drm/drm_framebuffer.c
drivers/gpu/drm/drm_ioc32.c
drivers/gpu/drm/drm_vblank.c
drivers/gpu/drm/i915/gvt/cmd_parser.c
drivers/gpu/drm/i915/gvt/display.c
drivers/gpu/drm/i915/gvt/gtt.c
drivers/gpu/drm/i915/gvt/handlers.c
drivers/gpu/drm/i915/gvt/kvmgt.c
drivers/gpu/drm/i915/gvt/scheduler.c
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_gem_execbuffer.c
drivers/gpu/drm/i915/i915_perf.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_cdclk.c
drivers/gpu/drm/i915/intel_engine_cs.c
drivers/gpu/drm/i915/intel_fbdev.c
drivers/gpu/drm/i915/selftests/i915_gem_dmabuf.c
drivers/gpu/drm/mediatek/Makefile
drivers/gpu/drm/mediatek/mtk_disp_color.c [new file with mode: 0644]
drivers/gpu/drm/mediatek/mtk_disp_ovl.c
drivers/gpu/drm/mediatek/mtk_drm_crtc.c
drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
drivers/gpu/drm/mediatek/mtk_drm_drv.c
drivers/gpu/drm/mediatek/mtk_drm_drv.h
drivers/gpu/drm/mediatek/mtk_drm_plane.c
drivers/gpu/drm/mediatek/mtk_dsi.c
drivers/gpu/drm/mediatek/mtk_hdmi.c
drivers/gpu/drm/radeon/atombios_encoders.c
drivers/gpu/drm/rockchip/cdn-dp-core.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_gem.c
drivers/mtd/Kconfig
drivers/mtd/Makefile
drivers/mtd/bcm47xxpart.c
drivers/mtd/chips/cfi_cmdset_0020.c
drivers/mtd/devices/Kconfig
drivers/mtd/devices/Makefile
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/mchp23k256.c [new file with mode: 0644]
drivers/mtd/devices/mtd_dataflash.c
drivers/mtd/devices/serial_flash_cmds.h
drivers/mtd/devices/st_spi_fsm.c
drivers/mtd/maps/physmap_of_gemini.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/atmel/nand-controller.c
drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/denali.c
drivers/mtd/nand/denali.h
drivers/mtd/nand/denali_dt.c
drivers/mtd/nand/denali_pci.c
drivers/mtd/nand/docg4.c
drivers/mtd/nand/fsl_elbc_nand.c
drivers/mtd/nand/fsl_ifc_nand.c
drivers/mtd/nand/fsmc_nand.c
drivers/mtd/nand/gpmi-nand/gpmi-lib.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.h
drivers/mtd/nand/hisi504_nand.c
drivers/mtd/nand/jz4780_nand.c
drivers/mtd/nand/mpc5121_nfc.c
drivers/mtd/nand/mtk_ecc.c
drivers/mtd/nand/mtk_ecc.h
drivers/mtd/nand/mtk_nand.c
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_micron.c
drivers/mtd/nand/orion_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/sunxi_nand.c
drivers/mtd/nand/tango_nand.c
drivers/mtd/nand/vf610_nfc.c
drivers/mtd/parsers/Kconfig [new file with mode: 0644]
drivers/mtd/parsers/Makefile [new file with mode: 0644]
drivers/mtd/parsers/parser_trx.c [new file with mode: 0644]
drivers/mtd/spi-nor/Kconfig
drivers/mtd/spi-nor/aspeed-smc.c
drivers/mtd/spi-nor/atmel-quadspi.c
drivers/mtd/spi-nor/cadence-quadspi.c
drivers/mtd/spi-nor/fsl-quadspi.c
drivers/mtd/spi-nor/hisi-sfc.c
drivers/mtd/spi-nor/intel-spi.c
drivers/mtd/spi-nor/mtk-quadspi.c
drivers/mtd/spi-nor/nxp-spifi.c
drivers/mtd/spi-nor/spi-nor.c
drivers/mtd/spi-nor/stm32-quadspi.c
drivers/mtd/tests/subpagetest.c
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/broadcom/bnxt/bnxt.h
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
drivers/net/ethernet/cisco/enic/vnic_dev.c
drivers/net/ethernet/hisilicon/hns/hns_enet.c
drivers/net/ethernet/mellanox/mlx5/core/Makefile
drivers/net/ethernet/mellanox/mlx5/core/accel/Makefile [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en_accel/Makefile [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
drivers/net/ethernet/mellanox/mlx5/core/fpga/Makefile [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c
drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
drivers/net/ethernet/mellanox/mlx5/core/ipoib/Makefile [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/lib/Makefile [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/lib/gid.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
drivers/net/ethernet/netronome/nfp/flower/metadata.c
drivers/net/ethernet/qlogic/qed/qed_iwarp.c
drivers/net/ethernet/sfc/ef10.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/tap.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/platform/x86/alienware-wmi.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/compal-laptop.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel_telemetry_debugfs.c
drivers/platform/x86/panasonic-laptop.c
drivers/platform/x86/peaq-wmi.c
drivers/platform/x86/samsung-laptop.c
drivers/platform/x86/silead_dmi.c
drivers/platform/x86/toshiba_acpi.c
drivers/power/supply/twl4030_charger.c
drivers/pwm/core.c
drivers/pwm/pwm-bfin.c
drivers/pwm/pwm-cros-ec.c
drivers/pwm/pwm-hibvt.c
drivers/pwm/pwm-meson.c
drivers/pwm/pwm-sun4i.c
drivers/pwm/pwm-tegra.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/class.c
drivers/rtc/interface.c
drivers/rtc/nvmem.c [new file with mode: 0644]
drivers/rtc/rtc-at91rm9200.c
drivers/rtc/rtc-brcmstb-waketimer.c [new file with mode: 0644]
drivers/rtc/rtc-core.h
drivers/rtc/rtc-dev.c
drivers/rtc/rtc-ds1307.c
drivers/rtc/rtc-ds3232.c
drivers/rtc/rtc-ftrtc010.c [new file with mode: 0644]
drivers/rtc/rtc-gemini.c [deleted file]
drivers/rtc/rtc-m41t80.c
drivers/rtc/rtc-mxc.c
drivers/rtc/rtc-nuc900.c
drivers/rtc/rtc-opal.c
drivers/rtc/rtc-pcf8563.c
drivers/rtc/rtc-rv8803.c
drivers/rtc/rtc-s3c.c
drivers/rtc/rtc-st-lpc.c
drivers/rtc/rtc-stm32.c
drivers/rtc/rtc-sysfs.c
drivers/staging/mt29f_spinand/mt29f_spinand.c
drivers/vfio/pci/vfio_pci.c
drivers/vfio/vfio.c
drivers/video/console/mdacon.c
drivers/video/fbdev/aty/atyfb_base.c
drivers/video/fbdev/core/fbmem.c
drivers/video/fbdev/fsl-diu-fb.c
drivers/video/fbdev/matrox/matroxfb_base.c
drivers/video/fbdev/omap/omapfb_main.c
drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c
drivers/video/fbdev/pxafb.c
drivers/video/fbdev/sh_mobile_lcdcfb.c
drivers/video/fbdev/uvesafb.c
drivers/video/fbdev/vermilion/cr_pll.c
fs/proc/internal.h
fs/proc/proc_sysctl.c
include/drm/bridge/dw_hdmi.h
include/linux/mtd/nand.h
include/linux/mtd/partitions.h
include/linux/mtd/spi-nor.h
include/linux/nvmem-provider.h
include/linux/rtc.h
include/linux/sysctl.h
include/linux/vfio.h
include/net/sock.h
kernel/kmod.c
kernel/module.c
net/bridge/br_mdb.c
net/core/datagram.c
net/ipv4/ipmr.c
samples/bpf/Makefile
samples/bpf/bpf_helpers.h [deleted file]
scripts/mod/modpost.c
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/bpf_endian.h
tools/testing/selftests/bpf/bpf_helpers.h [new file with mode: 0644]
tools/testing/selftests/timers/Makefile
tools/testing/selftests/timers/rtctest.c
tools/testing/selftests/timers/rtctest_setdate.c [new file with mode: 0644]
virt/kvm/vfio.c

index 3b5c3bca9186d13e8cf5f1911b2fc88424c6970f..f34e592301d1dbd9190d8557e43fd05bcde08f80 100644 (file)
@@ -229,6 +229,6 @@ KernelVersion:      4.1
 Contact:       linux-mtd@lists.infradead.org
 Description:
                For a partition, the offset of that partition from the start
-               of the master device in bytes. This attribute is absent on
-               main devices, so it can be used to distinguish between
-               partitions and devices that aren't partitions.
+               of the parent (another partition or a flash device) in bytes.
+               This attribute is absent on flash devices, so it can be used
+               to distinguish them from partitions.
index e593bbeb2115deb92966d593c18f002e5f269e41..504291d2e5c2e5e02b880738c7f453b527dee63c 100644 (file)
@@ -3,10 +3,23 @@
 Required properties:
   - compatible : should be one of the following:
       "altr,socfpga-denali-nand"            - for Altera SOCFPGA
+      "socionext,uniphier-denali-nand-v5a"  - for Socionext UniPhier (v5a)
+      "socionext,uniphier-denali-nand-v5b"  - for Socionext UniPhier (v5b)
   - reg : should contain registers location and length for data and reg.
   - reg-names: Should contain the reg names "nand_data" and "denali_reg"
   - interrupts : The interrupt number.
 
+Optional properties:
+  - nand-ecc-step-size: see nand.txt for details.  If present, the value must be
+      512        for "altr,socfpga-denali-nand"
+      1024       for "socionext,uniphier-denali-nand-v5a"
+      1024       for "socionext,uniphier-denali-nand-v5b"
+  - nand-ecc-strength: see nand.txt for details.  Valid values are:
+      8, 15      for "altr,socfpga-denali-nand"
+      8, 16, 24  for "socionext,uniphier-denali-nand-v5a"
+      8, 16      for "socionext,uniphier-denali-nand-v5b"
+  - nand-ecc-maximize: see nand.txt for details
+
 The device tree may optionally contain sub-nodes describing partitions of the
 address space. See partition.txt for more detail.
 
index 8c1528c421d47b2ca3995cff8a4f80af4942c8fb..59ddc61c10768dc1dfe73adbd5cf462e0dbc2da8 100644 (file)
@@ -1,7 +1,7 @@
 Error location module
 
 Required properties:
-- compatible: Must be "ti,am33xx-elm"
+- compatible: Must be "ti,am3352-elm"
 - reg: physical base address and size of the registers map.
 - interrupts: Interrupt number for the elm.
 
index 174f68c26c1b2a66a09c1b88dc7762d16ee54380..dd559045593d7be3cd4a984c643c7733bd7a3f76 100644 (file)
@@ -5,7 +5,7 @@ the GPMC controller with a name of "nand".
 
 All timing relevant properties as well as generic gpmc child properties are
 explained in a separate documents - please refer to
-Documentation/devicetree/bindings/bus/ti-gpmc.txt
+Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt
 
 For NAND specific properties such as ECC modes or bus width, please refer to
 Documentation/devicetree/bindings/mtd/nand.txt
index 4828c17bb784bd78d61d0766e80e6ef5ded9938c..131d3a74d0bd453f48c3f31e5487db4e3c71c6e1 100644 (file)
@@ -5,7 +5,7 @@ child nodes of the GPMC controller with a name of "nor".
 
 All timing relevant properties as well as generic GPMC child properties are
 explained in a separate documents. Please refer to
-Documentation/devicetree/bindings/bus/ti-gpmc.txt
+Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt
 
 Required properties:
 - bank-width:          Width of NOR flash in bytes. GPMC supports 8-bit and
@@ -28,7 +28,7 @@ Required properties:
 
 Optional properties:
 - gpmc,XXX             Additional GPMC timings and settings parameters. See
-                       Documentation/devicetree/bindings/bus/ti-gpmc.txt
+                       Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt
 
 Optional properties for partition table parsing:
 - #address-cells: should be set to 1
index 5d8fa527c496a1e29674064805c66461b20f2eed..b6e8bfd024f461902efbc09e52502fb7089c3048 100644 (file)
@@ -5,7 +5,7 @@ the GPMC controller with a name of "onenand".
 
 All timing relevant properties as well as generic gpmc child properties are
 explained in a separate documents - please refer to
-Documentation/devicetree/bindings/bus/ti-gpmc.txt
+Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt
 
 Required properties:
 
index d02acaff3c35e98476b91c26757f30355736cb3b..b289ef3c1b7e4a8ae798fc1b7f7db38880b3df23 100644 (file)
@@ -4,7 +4,12 @@ The GPMI nand controller provides an interface to control the
 NAND flash chips.
 
 Required properties:
-  - compatible : should be "fsl,<chip>-gpmi-nand"
+  - compatible : should be "fsl,<chip>-gpmi-nand", chip can be:
+    * imx23
+    * imx28
+    * imx6q
+    * imx6sx
+    * imx7d
   - reg : should contain registers location and length for gpmi and bch.
   - reg-names: Should contain the reg names "gpmi-nand" and "bch"
   - interrupts : BCH interrupt number.
@@ -13,6 +18,13 @@ Required properties:
     and GPMI DMA channel ID.
     Refer to dma.txt and fsl-mxs-dma.txt for details.
   - dma-names: Must be "rx-tx".
+  - clocks : clocks phandle and clock specifier corresponding to each clock
+    specified in clock-names.
+  - clock-names : The "gpmi_io" clock is always required. Which clocks are
+    exactly required depends on chip:
+    * imx23/imx28 : "gpmi_io"
+    * imx6q/sx : "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch"
+    * imx7d : "gpmi_io", "gpmi_bch_apb"
 
 Optional properties:
   - nand-on-flash-bbt: boolean to enable on flash bbt option if not
diff --git a/Documentation/devicetree/bindings/mtd/microchip,mchp23k256.txt b/Documentation/devicetree/bindings/mtd/microchip,mchp23k256.txt
new file mode 100644 (file)
index 0000000..7328eb9
--- /dev/null
@@ -0,0 +1,18 @@
+* MTD SPI driver for Microchip 23K256 (and similar) serial SRAM
+
+Required properties:
+- #address-cells, #size-cells : Must be present if the device has sub-nodes
+  representing partitions.
+- compatible : Must be one of "microchip,mchp23k256" or "microchip,mchp23lcv1024"
+- reg : Chip-Select number
+- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at
+
+Example:
+
+       spi-sram@0 {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               compatible = "microchip,mchp23k256";
+               reg = <0>;
+               spi-max-frequency = <20000000>;
+       };
index 069c192ed5c2f1b629b91a0ad4b98b73fdfda5d1..dbf9e054c11c0f3a68ba2783a042ca25e12d6b78 100644 (file)
@@ -12,7 +12,8 @@ tree nodes.
 
 The first part of NFC is NAND Controller Interface (NFI) HW.
 Required NFI properties:
-- compatible:                  Should be "mediatek,mtxxxx-nfc".
+- compatible:                  Should be one of "mediatek,mt2701-nfc",
+                               "mediatek,mt2712-nfc".
 - reg:                         Base physical address and size of NFI.
 - interrupts:                  Interrupts of NFI.
 - clocks:                      NFI required clocks.
@@ -141,7 +142,7 @@ Example:
 ==============
 
 Required BCH properties:
-- compatible:  Should be "mediatek,mtxxxx-ecc".
+- compatible:  Should be one of "mediatek,mt2701-ecc", "mediatek,mt2712-ecc".
 - reg:         Base physical address and size of ECC.
 - interrupts:  Interrupts of ECC.
 - clocks:      ECC required clocks.
index b05601600083d91c17c649b8cc5603011628f2a3..133f3813719c26398b6aede5b2126e0df33df28a 100644 (file)
@@ -21,7 +21,7 @@ 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".
+                 "hw_oob_first", "on-die".
                  Deprecated values:
                  "soft_bch": use "soft" and nand-ecc-algo instead
 - nand-ecc-algo: string, algorithm of NAND ECC.
index 81a224da63be54a4b7a5b248f57a985423b36213..36f3b769a62675cccac1a23ec8287e08bd91653e 100644 (file)
@@ -1,29 +1,49 @@
-Representing flash partitions in devicetree
+Flash partitions in device tree
+===============================
 
-Partitions can be represented by sub-nodes of an mtd device. This can be used
+Flash devices can be partitioned into one or more functional ranges (e.g. "boot
+code", "nvram", "kernel").
+
+Different devices may be partitioned in a different ways. Some may use a fixed
+flash layout set at production time. Some may use on-flash table that describes
+the geometry and naming/purpose of each functional region. It is also possible
+to see these methods mixed.
+
+To assist system software in locating partitions, we allow describing which
+method is used for a given flash device. To describe the method there should be
+a subnode of the flash device that is named 'partitions'. It must have a
+'compatible' property, which is used to identify the method to use.
+
+We currently only document a binding for fixed layouts.
+
+
+Fixed Partitions
+================
+
+Partitions can be represented by sub-nodes of a flash device. This can be used
 on platforms which have strong conventions about which portions of a flash are
 used for what purposes, but which don't use an on-flash partition table such
 as RedBoot.
 
-The partition table should be a subnode of the mtd node and should be named
+The partition table should be a subnode of the flash node and should be named
 'partitions'. This node should have the following property:
 - compatible : (required) must be "fixed-partitions"
 Partitions are then defined in subnodes of the partitions node.
 
-For backwards compatibility partitions as direct subnodes of the mtd device are
+For backwards compatibility partitions as direct subnodes of the flash device are
 supported. This use is discouraged.
 NOTE: also for backwards compatibility, direct subnodes that have a compatible
 string are not considered partitions, as they may be used for other bindings.
 
 #address-cells & #size-cells must both be present in the partitions subnode of the
-mtd device. There are two valid values for both:
+flash device. There are two valid values for both:
 <1>: for partitions that require a single 32-bit cell to represent their
      size/address (aka the value is below 4 GiB)
 <2>: for partitions that require two 32-bit cells to represent their
      size/address (aka the value is 4 GiB or greater).
 
 Required properties:
-- reg : The partition's offset and size within the mtd bank.
+- reg : The partition's offset and size within the flash
 
 Optional properties:
 - label : The label / name for this partition.  If omitted, the label is taken
index ace4a64b3695930254570d742704eb2abc888c37..f7da3d73ca1b2e15d71160b9ec811f2aad274e93 100644 (file)
@@ -9,7 +9,7 @@ the GPMC controller with an "ethernet" name.
 
 All timing relevant properties as well as generic GPMC child properties are
 explained in a separate documents. Please refer to
-Documentation/devicetree/bindings/bus/ti-gpmc.txt
+Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt
 
 For the properties relevant to the ethernet controller connected to the GPMC
 refer to the binding documentation of the device. For example, the documentation
@@ -43,7 +43,7 @@ Required properties:
 
 Optional properties:
 - gpmc,XXX             Additional GPMC timings and settings parameters. See
-                       Documentation/devicetree/bindings/bus/ti-gpmc.txt
+                       Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt
 
 Example:
 
index 5376a4468cb68a3e2e7e7fd1b82ef2b6910ea985..5b07bebbf6f7d9855733a4df0bacd6c057c1e87b 100644 (file)
@@ -2,7 +2,9 @@ Amlogic Meson PWM Controller
 ============================
 
 Required properties:
-- compatible: Shall contain "amlogic,meson8b-pwm" or "amlogic,meson-gxbb-pwm".
+- compatible: Shall contain "amlogic,meson8b-pwm"
+                         or "amlogic,meson-gxbb-pwm"
+                         or "amlogic,meson-gxbb-ao-pwm"
 - #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
   the cells format.
 
index 6dd040363e5ea918e1d743aef8bdef8bf6f3470f..3e6d55018d7ac40d8484e60e9566dedaff38e30d 100644 (file)
@@ -24,7 +24,7 @@ Example:
                compatible = "st,stm32-timers";
                reg = <0x40010000 0x400>;
                clocks = <&rcc 0 160>;
-               clock-names = "clk_int";
+               clock-names = "int";
 
                pwm {
                        compatible = "st,stm32-pwm";
index d6de643350223869dba6fdfa09ef668e17de784c..7e94b802395d677ec946c874ffaf58969d209a41 100644 (file)
@@ -8,6 +8,7 @@ Required Properties:
  - "renesas,pwm-r8a7791": for R-Car M2-W
  - "renesas,pwm-r8a7794": for R-Car E2
  - "renesas,pwm-r8a7795": for R-Car H3
+ - "renesas,pwm-r8a7796": for R-Car M3-W
 - reg: base address and length of the registers block for the PWM.
 - #pwm-cells: should be 2. See pwm.txt in this directory for a description of
   the cells format.
diff --git a/Documentation/devicetree/bindings/rtc/brcm,brcmstb-waketimer.txt b/Documentation/devicetree/bindings/rtc/brcm,brcmstb-waketimer.txt
new file mode 100644 (file)
index 0000000..1d990bc
--- /dev/null
@@ -0,0 +1,22 @@
+Broadcom STB wake-up Timer
+
+The Broadcom STB wake-up timer provides a 27Mhz resolution timer, with the
+ability to wake up the system from low-power suspend/standby modes.
+
+Required properties:
+- compatible     : should contain "brcm,brcmstb-waketimer"
+- reg            : the register start and length for the WKTMR block
+- interrupts     : The TIMER interrupt
+- interrupt-parent: The phandle to the Always-On (AON) Power Management (PM) L2
+                    interrupt controller node
+- clocks        : The phandle to the UPG fixed clock (27Mhz domain)
+
+Example:
+
+waketimer@f0411580 {
+       compatible = "brcm,brcmstb-waketimer";
+       reg = <0xf0411580 0x14>;
+       interrupts = <0x3>;
+       interrupt-parent = <&aon_pm_l2_intc>;
+       clocks = <&upg_fixed>;
+};
diff --git a/Documentation/devicetree/bindings/rtc/cortina,gemini.txt b/Documentation/devicetree/bindings/rtc/cortina,gemini.txt
deleted file mode 100644 (file)
index 4ce4e79..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-* Cortina Systems Gemini RTC
-
-Gemini SoC real-time clock.
-
-Required properties:
-- compatible : Should be "cortina,gemini-rtc"
-
-Examples:
-
-rtc@45000000 {
-       compatible = "cortina,gemini-rtc";
-       reg = <0x45000000 0x100>;
-       interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
-};
diff --git a/Documentation/devicetree/bindings/rtc/faraday,ftrtc010.txt b/Documentation/devicetree/bindings/rtc/faraday,ftrtc010.txt
new file mode 100644 (file)
index 0000000..e3938f5
--- /dev/null
@@ -0,0 +1,28 @@
+* Faraday Technology FTRTC010 Real Time Clock
+
+This RTC appears in for example the Storlink Gemini family of
+SoCs.
+
+Required properties:
+- compatible : Should be one of:
+  "faraday,ftrtc010"
+  "cortina,gemini-rtc", "faraday,ftrtc010"
+
+Optional properties:
+- clocks: when present should contain clock references to the
+  PCLK and EXTCLK clocks. Faraday calls the later CLK1HZ and
+  says the clock should be 1 Hz, but implementers actually seem
+  to choose different clocks here, like Cortina who chose
+  32768 Hz (a typical low-power clock).
+- clock-names: should name the clocks "PCLK" and "EXTCLK"
+  respectively.
+
+Examples:
+
+rtc@45000000 {
+       compatible = "cortina,gemini-rtc";
+       reg = <0x45000000 0x100>;
+       interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
+       clocks = <&foo 0>, <&foo 1>;
+       clock-names = "PCLK", "EXTCLK";
+};
index e2837b951237b90300af6679975bed765bffdff3..0a4c371a9b7a8ac46eb7db378da1ad1b117723d1 100644 (file)
@@ -1,17 +1,25 @@
 STM32 Real Time Clock
 
 Required properties:
-- compatible: "st,stm32-rtc".
+- compatible: can be either "st,stm32-rtc" or "st,stm32h7-rtc", depending on
+  the device is compatible with stm32(f4/f7) or stm32h7.
 - reg: address range of rtc register set.
-- clocks: reference to the clock entry ck_rtc.
+- clocks: can use up to two clocks, depending on part used:
+  - "rtc_ck": RTC clock source.
+    It is required on stm32(f4/f7) and stm32h7.
+  - "pclk": RTC APB interface clock.
+    It is not present on stm32(f4/f7).
+    It is required on stm32h7.
+- clock-names: must be "rtc_ck" and "pclk".
+    It is required only on stm32h7.
 - interrupt-parent: phandle for the interrupt controller.
 - interrupts: rtc alarm interrupt.
 - st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain
   (RTC registers) write protection.
 
-Optional properties (to override default ck_rtc parent clock):
-- assigned-clocks: reference to the ck_rtc clock entry.
-- assigned-clock-parents: phandle of the new parent clock of ck_rtc.
+Optional properties (to override default rtc_ck parent clock):
+- assigned-clocks: reference to the rtc_ck clock entry.
+- assigned-clock-parents: phandle of the new parent clock of rtc_ck.
 
 Example:
 
@@ -25,3 +33,17 @@ Example:
                interrupts = <17 1>;
                st,syscfg = <&pwrcfg>;
        };
+
+       rtc: rtc@58004000 {
+               compatible = "st,stm32h7-rtc";
+               reg = <0x58004000 0x400>;
+               clocks = <&rcc RTCAPB_CK>, <&rcc RTC_CK>;
+               clock-names = "pclk", "rtc_ck";
+               assigned-clocks = <&rcc RTC_CK>;
+               assigned-clock-parents = <&rcc LSE_CK>;
+               interrupt-parent = <&exti>;
+               interrupts = <17 1>;
+               interrupt-names = "alarm";
+               st,syscfg = <&pwrcfg>;
+               status = "disabled";
+       };
index 196ba17cc344f61a44ff84c85c89ea1db2d73b80..1be0b6f9e0cb223a6e781d119b236896f186499d 100644 (file)
@@ -44,8 +44,7 @@ timeval of SO_TIMESTAMP (ms).
 Supports multiple types of timestamp requests. As a result, this
 socket option takes a bitmap of flags, not a boolean. In
 
-  err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, (void *) val,
-                   sizeof(val));
+  err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
 
 val is an integer with any of the following bits set. Setting other
 bit returns EINVAL and does not change the current state.
@@ -249,8 +248,7 @@ setsockopt to receive timestamps:
 
   __u32 val = SOF_TIMESTAMPING_SOFTWARE |
              SOF_TIMESTAMPING_OPT_ID /* or any other flag */;
-  err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, (void *) val,
-                   sizeof(val));
+  err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
 
 
 1.4 Bytestream Timestamps
index 789b27c6ec996735932131315ee7a5b9aebd0c7d..8fbf0aa3ba2d52eb17b45ed8148eb504101bb711 100644 (file)
@@ -1,4 +1,6 @@
+======================================
 Pulse Width Modulation (PWM) interface
+======================================
 
 This provides an overview about the Linux PWM interface
 
@@ -16,7 +18,7 @@ Users of the legacy PWM API use unique IDs to refer to PWM devices.
 
 Instead of referring to a PWM device via its unique ID, board setup code
 should instead register a static mapping that can be used to match PWM
-consumers to providers, as given in the following example:
+consumers to providers, as given in the following example::
 
        static struct pwm_lookup board_pwm_lookup[] = {
                PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL,
@@ -40,9 +42,9 @@ New users should use the pwm_get() function and pass to it the consumer
 device or a consumer name. pwm_put() is used to free the PWM device. Managed
 variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist.
 
-After being requested, a PWM has to be configured using:
+After being requested, a PWM has to be configured using::
 
-int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
+       int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
 
 This API controls both the PWM period/duty_cycle config and the
 enable/disable state.
@@ -72,11 +74,14 @@ interface is provided to use the PWMs from userspace. It is exposed at
 pwmchipN, where N is the base of the PWM chip. Inside the directory you
 will find:
 
-npwm - The number of PWM channels this chip supports (read-only).
+  npwm
+    The number of PWM channels this chip supports (read-only).
 
-export - Exports a PWM channel for use with sysfs (write-only).
+  export
+    Exports a PWM channel for use with sysfs (write-only).
 
-unexport - Unexports a PWM channel from sysfs (write-only).
+  unexport
+   Unexports a PWM channel from sysfs (write-only).
 
 The PWM channels are numbered using a per-chip index from 0 to npwm-1.
 
@@ -84,21 +89,26 @@ When a PWM channel is exported a pwmX directory will be created in the
 pwmchipN directory it is associated with, where X is the number of the
 channel that was exported. The following properties will then be available:
 
-period - The total period of the PWM signal (read/write).
-       Value is in nanoseconds and is the sum of the active and inactive
-       time of the PWM.
+  period
+    The total period of the PWM signal (read/write).
+    Value is in nanoseconds and is the sum of the active and inactive
+    time of the PWM.
 
-duty_cycle - The active time of the PWM signal (read/write).
-       Value is in nanoseconds and must be less than the period.
+  duty_cycle
+    The active time of the PWM signal (read/write).
+    Value is in nanoseconds and must be less than the period.
 
-polarity - Changes the polarity of the PWM signal (read/write).
-       Writes to this property only work if the PWM chip supports changing
-       the polarity. The polarity can only be changed if the PWM is not
-       enabled. Value is the string "normal" or "inversed".
+  polarity
+    Changes the polarity of the PWM signal (read/write).
+    Writes to this property only work if the PWM chip supports changing
+    the polarity. The polarity can only be changed if the PWM is not
+    enabled. Value is the string "normal" or "inversed".
 
-enable - Enable/disable the PWM signal (read/write).
-       0 - disabled
-       1 - enabled
+  enable
+    Enable/disable the PWM signal (read/write).
+
+       - 0 - disabled
+       - 1 - enabled
 
 Implementing a PWM driver
 -------------------------
index ddc366026e00f60b8491fe50587e35836656835b..c0c977445fb9ca732e45acf1ddbf787f9398315d 100644 (file)
@@ -1,6 +1,6 @@
-
-       Real Time Clock (RTC) Drivers for Linux
-       =======================================
+=======================================
+Real Time Clock (RTC) Drivers for Linux
+=======================================
 
 When Linux developers talk about a "Real Time Clock", they usually mean
 something that tracks wall clock time and is battery backed so that it
@@ -32,8 +32,8 @@ only issue an alarm up to 24 hours in the future, other hardware may
 be able to schedule one any time in the upcoming century.
 
 
-       Old PC/AT-Compatible driver:  /dev/rtc
-       --------------------------------------
+Old PC/AT-Compatible driver:  /dev/rtc
+--------------------------------------
 
 All PCs (even Alpha machines) have a Real Time Clock built into them.
 Usually they are built into the chipset of the computer, but some may
@@ -105,8 +105,8 @@ that will be using this driver.  See the code at the end of this document.
 (The original /dev/rtc driver was written by Paul Gortmaker.)
 
 
-       New portable "RTC Class" drivers:  /dev/rtcN
-       --------------------------------------------
+New portable "RTC Class" drivers:  /dev/rtcN
+--------------------------------------------
 
 Because Linux supports many non-ACPI and non-PC platforms, some of which
 have more than one RTC style clock, it needed a more portable solution
@@ -136,35 +136,39 @@ a high functionality RTC is integrated into the SOC.  That system might read
 the system clock from the discrete RTC, but use the integrated one for all
 other tasks, because of its greater functionality.
 
-SYSFS INTERFACE
+SYSFS interface
 ---------------
 
 The sysfs interface under /sys/class/rtc/rtcN provides access to various
 rtc attributes without requiring the use of ioctls. All dates and times
 are in the RTC's timezone, rather than in system time.
 
-date:                   RTC-provided date
-hctosys:        1 if the RTC provided the system time at boot via the
+================ ==============================================================
+date            RTC-provided date
+hctosys         1 if the RTC provided the system time at boot via the
                 CONFIG_RTC_HCTOSYS kernel option, 0 otherwise
-max_user_freq:  The maximum interrupt rate an unprivileged user may request
+max_user_freq   The maximum interrupt rate an unprivileged user may request
                 from this RTC.
-name:           The name of the RTC corresponding to this sysfs directory
-since_epoch:    The number of seconds since the epoch according to the RTC
-time:           RTC-provided time
-wakealarm:      The time at which the clock will generate a system wakeup
+name            The name of the RTC corresponding to this sysfs directory
+since_epoch     The number of seconds since the epoch according to the RTC
+time            RTC-provided time
+wakealarm       The time at which the clock will generate a system wakeup
                 event. This is a one shot wakeup event, so must be reset
-                after wake if a daily wakeup is required. Format is seconds since
-                the epoch by default, or if there's a leading +, seconds in the
-                future, or if there is a leading +=, seconds ahead of the current
-                alarm.
-offset:                 The amount which the rtc clock has been adjusted in firmware.
+                after wake if a daily wakeup is required. Format is seconds
+                since the epoch by default, or if there's a leading +, seconds
+                in the future, or if there is a leading +=, seconds ahead of
+                the current alarm.
+offset          The amount which the rtc clock has been adjusted in firmware.
                 Visible only if the driver supports clock offset adjustment.
                 The unit is parts per billion, i.e. The number of clock ticks
                 which are added to or removed from the rtc's base clock per
                 billion ticks. A positive value makes a day pass more slowly,
                 longer, and a negative value makes a day pass more quickly.
+*/nvmem                 The non volatile storage exported as a raw file, as described
+                in Documentation/nvmem/nvmem.txt
+================ ==============================================================
 
-IOCTL INTERFACE
+IOCTL interface
 ---------------
 
 The ioctl() calls supported by /dev/rtc are also supported by the RTC class
index 1347726cf9d531705926c4f95aeadb8962d9ee64..7d9bd4a041aff9254ce43f85ca173da777087c19 100644 (file)
@@ -1255,7 +1255,7 @@ L:        linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 T:     git git://github.com/ulli-kroll/linux.git
 S:     Maintained
 F:     arch/arm/mach-gemini/
-F:     drivers/rtc/rtc-gemini.c
+F:     drivers/rtc/rtc-ftrtc010.c
 
 ARM/CSR SIRFPRIMA2 MACHINE SUPPORT
 M:     Barry Song <baohua@kernel.org>
@@ -3974,6 +3974,12 @@ M:       Pali Rohár <pali.rohar@gmail.com>
 S:     Maintained
 F:     drivers/platform/x86/dell-wmi.c
 
+DENALI NAND DRIVER
+M:     Masahiro Yamada <yamada.masahiro@socionext.com>
+L:     linux-mtd@lists.infradead.org
+S:     Supported
+F:     drivers/mtd/nand/denali*
+
 DESIGNWARE USB2 DRD IP DRIVER
 M:     John Youn <johnyoun@synopsys.com>
 L:     linux-usb@vger.kernel.org
@@ -12464,7 +12470,8 @@ M:      Marek Vasut <marek.vasut@gmail.com>
 L:     linux-mtd@lists.infradead.org
 W:     http://www.linux-mtd.infradead.org/
 Q:     http://patchwork.ozlabs.org/project/linux-mtd/list/
-T:     git git://github.com/spi-nor/linux.git
+T:     git git://git.infradead.org/linux-mtd.git spi-nor/fixes
+T:     git git://git.infradead.org/l2-mtd.git spi-nor/next
 S:     Maintained
 F:     drivers/mtd/spi-nor/
 F:     include/linux/mtd/spi-nor.h
index 06ef9947cf7c01ad7b226b80e741d0e174e207f3..547947ff87de9fb5c861b38187ed8d6343b2a597 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -622,6 +622,9 @@ include arch/$(SRCARCH)/Makefile
 
 KBUILD_CFLAGS  += $(call cc-option,-fno-delete-null-pointer-checks,)
 KBUILD_CFLAGS  += $(call cc-disable-warning,frame-address,)
+KBUILD_CFLAGS  += $(call cc-disable-warning, format-truncation)
+KBUILD_CFLAGS  += $(call cc-disable-warning, format-overflow)
+KBUILD_CFLAGS  += $(call cc-disable-warning, int-in-bool-context)
 
 ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
 KBUILD_CFLAGS  += $(call cc-option,-Oz,-Os)
index 99f9a4beb85928ee5bb12f38f9dc7fdd26ec0546..67fe19e5a9c6e65777c528e9a333e2c2247233fd 100644 (file)
@@ -161,7 +161,7 @@ struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
        int ret;
 
        if (!panel)
-               return ERR_PTR(EINVAL);
+               return ERR_PTR(-EINVAL);
 
        panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
                                    GFP_KERNEL);
index fc8ef42203ec000c18c6206dcf141d21f04f79b1..b3ef4f1c2630fd5973132f43c809b1dfe05a169c 100644 (file)
@@ -832,6 +832,7 @@ unlock:
                drm_atomic_clean_old_fb(dev, plane_mask, ret);
 
        if (ret == -EDEADLK) {
+               drm_atomic_state_clear(state);
                drm_modeset_backoff(&ctx);
                goto retry;
        }
index 0b2d8c4a2fa528dafa96a22cd75768c643db205c..d1f2028520288261e4ad7c6344ea94e80178f3db 100644 (file)
@@ -112,6 +112,9 @@ static int compat_drm_version(struct file *file, unsigned int cmd,
        v32.version_major = v.version_major;
        v32.version_minor = v.version_minor;
        v32.version_patchlevel = v.version_patchlevel;
+       v32.name_len = v.name_len;
+       v32.date_len = v.date_len;
+       v32.desc_len = v.desc_len;
        if (copy_to_user((void __user *)arg, &v32, sizeof(v32)))
                return -EFAULT;
        return 0;
index 463e4d81fb0ddb9807b84d1039288795c4842187..e9f33cd805dd6bfaa746800d2d67e53ec578ae18 100644 (file)
@@ -242,7 +242,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
         * Otherwise reinitialize delayed at next vblank interrupt and assign 0
         * for now, to mark the vblanktimestamp as invalid.
         */
-       if (!rc && in_vblank_irq)
+       if (!rc && !in_vblank_irq)
                t_vblank = (struct timeval) {0, 0};
 
        store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
index 51241de5e7a74ce68f7fdf732165c5ec1373f491..713848c3634946bea6c9805a3a705c22c1b8c08c 100644 (file)
@@ -2536,6 +2536,11 @@ static int scan_workload(struct intel_vgpu_workload *workload)
                gma_head == gma_tail)
                return 0;
 
+       if (!intel_gvt_ggtt_validate_range(s.vgpu, s.ring_start, s.ring_size)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
        ret = ip_gma_set(&s, gma_head);
        if (ret)
                goto out;
@@ -2579,6 +2584,11 @@ static int scan_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
        s.rb_va = wa_ctx->indirect_ctx.shadow_va;
        s.workload = workload;
 
+       if (!intel_gvt_ggtt_validate_range(s.vgpu, s.ring_start, s.ring_size)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
        ret = ip_gma_set(&s, gma_head);
        if (ret)
                goto out;
index e0261fcc5b504ef6d91766bc40b999a5a9d058bc..2deb05f618fb11054064122a4b86f468d148c73b 100644 (file)
@@ -197,6 +197,12 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
                        (TRANS_DDI_BPC_8 | TRANS_DDI_MODE_SELECT_DP_SST |
                        (PORT_B << TRANS_DDI_PORT_SHIFT) |
                        TRANS_DDI_FUNC_ENABLE);
+               if (IS_BROADWELL(dev_priv)) {
+                       vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_B)) &=
+                               ~PORT_CLK_SEL_MASK;
+                       vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_B)) |=
+                               PORT_CLK_SEL_LCPLL_810;
+               }
                vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_B)) |= DDI_BUF_CTL_ENABLE;
                vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_B)) &= ~DDI_BUF_IS_IDLE;
                vgpu_vreg(vgpu, SDEISR) |= SDE_PORTB_HOTPLUG_CPT;
@@ -211,6 +217,12 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
                        (TRANS_DDI_BPC_8 | TRANS_DDI_MODE_SELECT_DP_SST |
                        (PORT_C << TRANS_DDI_PORT_SHIFT) |
                        TRANS_DDI_FUNC_ENABLE);
+               if (IS_BROADWELL(dev_priv)) {
+                       vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_C)) &=
+                               ~PORT_CLK_SEL_MASK;
+                       vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_C)) |=
+                               PORT_CLK_SEL_LCPLL_810;
+               }
                vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_C)) |= DDI_BUF_CTL_ENABLE;
                vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_C)) &= ~DDI_BUF_IS_IDLE;
                vgpu_vreg(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDIC_DETECTED;
@@ -225,6 +237,12 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
                        (TRANS_DDI_BPC_8 | TRANS_DDI_MODE_SELECT_DP_SST |
                        (PORT_D << TRANS_DDI_PORT_SHIFT) |
                        TRANS_DDI_FUNC_ENABLE);
+               if (IS_BROADWELL(dev_priv)) {
+                       vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_D)) &=
+                               ~PORT_CLK_SEL_MASK;
+                       vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_D)) |=
+                               PORT_CLK_SEL_LCPLL_810;
+               }
                vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_D)) |= DDI_BUF_CTL_ENABLE;
                vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_D)) &= ~DDI_BUF_IS_IDLE;
                vgpu_vreg(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDID_DETECTED;
@@ -244,6 +262,10 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
 
                vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_A)) |= DDI_INIT_DISPLAY_DETECTED;
        }
+
+       /* Clear host CRT status, so guest couldn't detect this host CRT. */
+       if (IS_BROADWELL(dev_priv))
+               vgpu_vreg(vgpu, PCH_ADPA) &= ~ADPA_CRT_HOTPLUG_MONITOR_MASK;
 }
 
 static void clean_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num)
index 66374dba3b1a29800f5a03eda75d4282c0852337..6166e34d892b11c3bd6d219169bde81af1d112f0 100644 (file)
@@ -2259,6 +2259,8 @@ int intel_gvt_init_gtt(struct intel_gvt *gvt)
                ret = setup_spt_oos(gvt);
                if (ret) {
                        gvt_err("fail to initialize SPT oos\n");
+                       dma_unmap_page(dev, daddr, 4096, PCI_DMA_BIDIRECTIONAL);
+                       __free_page(gvt->gtt.scratch_ggtt_page);
                        return ret;
                }
        }
index 1414d7e6148d2aceb31fe71fbf7d96e37e718573..17febe830ff6984e06bb81cb91601a76b67d5f2a 100644 (file)
@@ -367,21 +367,24 @@ static int lcpll_ctl_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,
 static int dpy_reg_mmio_read(struct intel_vgpu *vgpu, unsigned int offset,
                void *p_data, unsigned int bytes)
 {
-       *(u32 *)p_data = (1 << 17);
-       return 0;
-}
-
-static int dpy_reg_mmio_read_2(struct intel_vgpu *vgpu, unsigned int offset,
-               void *p_data, unsigned int bytes)
-{
-       *(u32 *)p_data = 3;
-       return 0;
-}
+       switch (offset) {
+       case 0xe651c:
+       case 0xe661c:
+       case 0xe671c:
+       case 0xe681c:
+               vgpu_vreg(vgpu, offset) = 1 << 17;
+               break;
+       case 0xe6c04:
+               vgpu_vreg(vgpu, offset) = 0x3;
+               break;
+       case 0xe6e1c:
+               vgpu_vreg(vgpu, offset) = 0x2f << 16;
+               break;
+       default:
+               return -EINVAL;
+       }
 
-static int dpy_reg_mmio_read_3(struct intel_vgpu *vgpu, unsigned int offset,
-               void *p_data, unsigned int bytes)
-{
-       *(u32 *)p_data = (0x2f << 16);
+       read_vreg(vgpu, offset, p_data, bytes);
        return 0;
 }
 
@@ -1925,7 +1928,7 @@ static int init_generic_mmio_info(struct intel_gvt *gvt)
        MMIO_F(_PCH_DPD_AUX_CH_CTL, 6 * 4, 0, 0, 0, D_PRE_SKL, NULL,
                dp_aux_ch_ctl_mmio_write);
 
-       MMIO_RO(PCH_ADPA, D_ALL, 0, ADPA_CRT_HOTPLUG_MONITOR_MASK, NULL, pch_adpa_mmio_write);
+       MMIO_DH(PCH_ADPA, D_PRE_SKL, NULL, pch_adpa_mmio_write);
 
        MMIO_DH(_PCH_TRANSACONF, D_ALL, NULL, transconf_mmio_write);
        MMIO_DH(_PCH_TRANSBCONF, D_ALL, NULL, transconf_mmio_write);
@@ -2011,8 +2014,8 @@ static int init_generic_mmio_info(struct intel_gvt *gvt)
        MMIO_DH(0xe661c, D_ALL, dpy_reg_mmio_read, NULL);
        MMIO_DH(0xe671c, D_ALL, dpy_reg_mmio_read, NULL);
        MMIO_DH(0xe681c, D_ALL, dpy_reg_mmio_read, NULL);
-       MMIO_DH(0xe6c04, D_ALL, dpy_reg_mmio_read_2, NULL);
-       MMIO_DH(0xe6e1c, D_ALL, dpy_reg_mmio_read_3, NULL);
+       MMIO_DH(0xe6c04, D_ALL, dpy_reg_mmio_read, NULL);
+       MMIO_DH(0xe6e1c, D_ALL, dpy_reg_mmio_read, NULL);
 
        MMIO_RO(PCH_PORT_HOTPLUG, D_ALL, 0,
                PORTA_HOTPLUG_STATUS_MASK
index 1ae0b4083ce10b933e877ab7cafdd41b543a2b60..fd0c85f9ef3c369390abe200f8511f184c5c257a 100644 (file)
@@ -232,16 +232,20 @@ static void gvt_cache_destroy(struct intel_vgpu *vgpu)
        struct device *dev = mdev_dev(vgpu->vdev.mdev);
        unsigned long gfn;
 
-       mutex_lock(&vgpu->vdev.cache_lock);
-       while ((node = rb_first(&vgpu->vdev.cache))) {
+       for (;;) {
+               mutex_lock(&vgpu->vdev.cache_lock);
+               node = rb_first(&vgpu->vdev.cache);
+               if (!node) {
+                       mutex_unlock(&vgpu->vdev.cache_lock);
+                       break;
+               }
                dma = rb_entry(node, struct gvt_dma, node);
                gvt_dma_unmap_iova(vgpu, dma->iova);
                gfn = dma->gfn;
-
-               vfio_unpin_pages(dev, &gfn, 1);
                __gvt_cache_remove_entry(vgpu, dma);
+               mutex_unlock(&vgpu->vdev.cache_lock);
+               vfio_unpin_pages(dev, &gfn, 1);
        }
-       mutex_unlock(&vgpu->vdev.cache_lock);
 }
 
 static struct intel_vgpu_type *intel_gvt_find_vgpu_type(struct intel_gvt *gvt,
index 488fdea348a979e411258d6317748fe2bdd5e242..4f7057d62d88b393ce77670f9100bc2d3b246014 100644 (file)
@@ -174,15 +174,6 @@ static int shadow_context_status_change(struct notifier_block *nb,
                atomic_set(&workload->shadow_ctx_active, 1);
                break;
        case INTEL_CONTEXT_SCHEDULE_OUT:
-               /* If the status is -EINPROGRESS means this workload
-                * doesn't meet any issue during dispatching so when
-                * get the SCHEDULE_OUT set the status to be zero for
-                * good. If the status is NOT -EINPROGRESS means there
-                * is something wrong happened during dispatching and
-                * the status should not be set to zero
-                */
-               if (workload->status == -EINPROGRESS)
-                       workload->status = 0;
                atomic_set(&workload->shadow_ctx_active, 0);
                break;
        default:
@@ -427,6 +418,18 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id)
                wait_event(workload->shadow_ctx_status_wq,
                           !atomic_read(&workload->shadow_ctx_active));
 
+               /* If this request caused GPU hang, req->fence.error will
+                * be set to -EIO. Use -EIO to set workload status so
+                * that when this request caused GPU hang, didn't trigger
+                * context switch interrupt to guest.
+                */
+               if (likely(workload->status == -EINPROGRESS)) {
+                       if (workload->req->fence.error == -EIO)
+                               workload->status = -EIO;
+                       else
+                               workload->status = 0;
+               }
+
                i915_gem_request_put(fetch_and_zero(&workload->req));
 
                if (!workload->status && !vgpu->resetting) {
@@ -464,8 +467,6 @@ struct workload_thread_param {
        int ring_id;
 };
 
-static DEFINE_MUTEX(scheduler_mutex);
-
 static int workload_thread(void *priv)
 {
        struct workload_thread_param *p = (struct workload_thread_param *)priv;
@@ -497,8 +498,6 @@ static int workload_thread(void *priv)
                if (!workload)
                        break;
 
-               mutex_lock(&scheduler_mutex);
-
                gvt_dbg_sched("ring id %d next workload %p vgpu %d\n",
                                workload->ring_id, workload,
                                workload->vgpu->id);
@@ -537,9 +536,6 @@ complete:
                                        FORCEWAKE_ALL);
 
                intel_runtime_pm_put(gvt->dev_priv);
-
-               mutex_unlock(&scheduler_mutex);
-
        }
        return 0;
 }
index 3f44076ec8a0dbb8db76afcc2507f81c9916ba7e..00d8967c8512048f5fb46f629c9c76ace1161bbf 100644 (file)
@@ -3087,7 +3087,7 @@ static void intel_connector_info(struct seq_file *m,
                           connector->display_info.cea_rev);
        }
 
-       if (!intel_encoder || intel_encoder->type == INTEL_OUTPUT_DP_MST)
+       if (!intel_encoder)
                return;
 
        switch (connector->connector_type) {
index ee2325b180e75b06205b0b8d6d992250ec08b8fd..fc307e03943c829c8355d7da4c80db41c0838570 100644 (file)
@@ -1132,10 +1132,12 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
         * and the registers being closely associated.
         *
         * According to chipset errata, on the 965GM, MSI interrupts may
-        * be lost or delayed, but we use them anyways to avoid
-        * stuck interrupts on some machines.
+        * be lost or delayed, and was defeatured. MSI interrupts seem to
+        * get lost on g4x as well, and interrupt delivery seems to stay
+        * properly dead afterwards. So we'll just disable them for all
+        * pre-gen5 chipsets.
         */
-       if (!IS_I945G(dev_priv) && !IS_I945GM(dev_priv)) {
+       if (INTEL_GEN(dev_priv) >= 5) {
                if (pci_enable_msi(pdev) < 0)
                        DRM_DEBUG_DRIVER("can't enable MSI");
        }
index 9337446f10682db82fff2fca76b7a4bd6cef2149..054b2e54cdafd765e82f9a86276c863744f3cd14 100644 (file)
@@ -288,20 +288,26 @@ static int eb_create(struct i915_execbuffer *eb)
                 * direct lookup.
                 */
                do {
+                       unsigned int flags;
+
+                       /* While we can still reduce the allocation size, don't
+                        * raise a warning and allow the allocation to fail.
+                        * On the last pass though, we want to try as hard
+                        * as possible to perform the allocation and warn
+                        * if it fails.
+                        */
+                       flags = GFP_TEMPORARY;
+                       if (size > 1)
+                               flags |= __GFP_NORETRY | __GFP_NOWARN;
+
                        eb->buckets = kzalloc(sizeof(struct hlist_head) << size,
-                                             GFP_TEMPORARY |
-                                             __GFP_NORETRY |
-                                             __GFP_NOWARN);
+                                             flags);
                        if (eb->buckets)
                                break;
                } while (--size);
 
-               if (unlikely(!eb->buckets)) {
-                       eb->buckets = kzalloc(sizeof(struct hlist_head),
-                                             GFP_TEMPORARY);
-                       if (unlikely(!eb->buckets))
-                               return -ENOMEM;
-               }
+               if (unlikely(!size))
+                       return -ENOMEM;
 
                eb->lut_size = size;
        } else {
@@ -452,7 +458,7 @@ eb_add_vma(struct i915_execbuffer *eb,
                        return err;
        }
 
-       if (eb->lut_size >= 0) {
+       if (eb->lut_size > 0) {
                vma->exec_handle = entry->handle;
                hlist_add_head(&vma->exec_node,
                               &eb->buckets[hash_32(entry->handle,
@@ -894,7 +900,7 @@ static void eb_release_vmas(const struct i915_execbuffer *eb)
 static void eb_reset_vmas(const struct i915_execbuffer *eb)
 {
        eb_release_vmas(eb);
-       if (eb->lut_size >= 0)
+       if (eb->lut_size > 0)
                memset(eb->buckets, 0,
                       sizeof(struct hlist_head) << eb->lut_size);
 }
@@ -903,7 +909,7 @@ static void eb_destroy(const struct i915_execbuffer *eb)
 {
        GEM_BUG_ON(eb->reloc_cache.rq);
 
-       if (eb->lut_size >= 0)
+       if (eb->lut_size > 0)
                kfree(eb->buckets);
 }
 
@@ -2180,8 +2186,11 @@ i915_gem_do_execbuffer(struct drm_device *dev,
                }
        }
 
-       if (eb_create(&eb))
-               return -ENOMEM;
+       err = eb_create(&eb);
+       if (err)
+               goto err_out_fence;
+
+       GEM_BUG_ON(!eb.lut_size);
 
        /*
         * Take a local wakeref for preparing to dispatch the execbuf as
@@ -2340,6 +2349,7 @@ err_unlock:
 err_rpm:
        intel_runtime_pm_put(eb.i915);
        eb_destroy(&eb);
+err_out_fence:
        if (out_fence_fd != -1)
                put_unused_fd(out_fence_fd);
 err_in_fence:
index 38c44407bafc55e0d47b2446a990ca86f05f4344..9cd22f83b0cfaee680ed06c5bde67db6fc89d0fa 100644 (file)
@@ -2067,10 +2067,6 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
                        return ret;
        }
 
-       ret = alloc_oa_buffer(dev_priv);
-       if (ret)
-               goto err_oa_buf_alloc;
-
        /* PRM - observability performance counters:
         *
         *   OACONTROL, performance counter enable, note:
@@ -2086,6 +2082,10 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
        intel_runtime_pm_get(dev_priv);
        intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
 
+       ret = alloc_oa_buffer(dev_priv);
+       if (ret)
+               goto err_oa_buf_alloc;
+
        ret = dev_priv->perf.oa.ops.enable_metric_set(dev_priv);
        if (ret)
                goto err_enable;
@@ -2097,11 +2097,11 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
        return 0;
 
 err_enable:
-       intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
-       intel_runtime_pm_put(dev_priv);
        free_oa_buffer(dev_priv);
 
 err_oa_buf_alloc:
+       intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
+       intel_runtime_pm_put(dev_priv);
        if (stream->ctx)
                oa_put_render_ctx_id(stream);
 
index c8647cfa81baab5c88f5d5c592814b1086a8816c..64cc674b652ac038f14b9c8ad860601e43a51ae8 100644 (file)
@@ -1802,7 +1802,7 @@ enum skl_disp_power_wells {
 #define   POST_CURSOR_2(x)             ((x) << 6)
 #define   POST_CURSOR_2_MASK           (0x3F << 6)
 #define   CURSOR_COEFF(x)              ((x) << 0)
-#define   CURSOR_COEFF_MASK            (0x3F << 6)
+#define   CURSOR_COEFF_MASK            (0x3F << 0)
 
 #define _CNL_PORT_TX_DW5_GRP_AE                0x162354
 #define _CNL_PORT_TX_DW5_GRP_B         0x1623D4
index b8914db7d2e1bcba3168bc680f35b8e1bb4fb7ee..1241e5891b29511d6db6de2fcd6daaf6d100c531 100644 (file)
@@ -491,6 +491,14 @@ static void vlv_set_cdclk(struct drm_i915_private *dev_priv,
        int cdclk = cdclk_state->cdclk;
        u32 val, cmd;
 
+       /* There are cases where we can end up here with power domains
+        * off and a CDCLK frequency other than the minimum, like when
+        * issuing a modeset without actually changing any display after
+        * a system suspend.  So grab the PIPE-A domain, which covers
+        * the HW blocks needed for the following programming.
+        */
+       intel_display_power_get(dev_priv, POWER_DOMAIN_PIPE_A);
+
        if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */
                cmd = 2;
        else if (cdclk == 266667)
@@ -549,6 +557,8 @@ static void vlv_set_cdclk(struct drm_i915_private *dev_priv,
        intel_update_cdclk(dev_priv);
 
        vlv_program_pfi_credits(dev_priv);
+
+       intel_display_power_put(dev_priv, POWER_DOMAIN_PIPE_A);
 }
 
 static void chv_set_cdclk(struct drm_i915_private *dev_priv,
@@ -568,6 +578,14 @@ static void chv_set_cdclk(struct drm_i915_private *dev_priv,
                return;
        }
 
+       /* There are cases where we can end up here with power domains
+        * off and a CDCLK frequency other than the minimum, like when
+        * issuing a modeset without actually changing any display after
+        * a system suspend.  So grab the PIPE-A domain, which covers
+        * the HW blocks needed for the following programming.
+        */
+       intel_display_power_get(dev_priv, POWER_DOMAIN_PIPE_A);
+
        /*
         * Specs are full of misinformation, but testing on actual
         * hardware has shown that we just need to write the desired
@@ -590,6 +608,8 @@ static void chv_set_cdclk(struct drm_i915_private *dev_priv,
        intel_update_cdclk(dev_priv);
 
        vlv_program_pfi_credits(dev_priv);
+
+       intel_display_power_put(dev_priv, POWER_DOMAIN_PIPE_A);
 }
 
 static int bdw_calc_cdclk(int max_pixclk)
index a4487c5b7e37d209bb655cd629709b0ed03faa29..5b4de719bec3dc2faf9fefeaf7dd46cceb8f2679 100644 (file)
@@ -821,9 +821,10 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
        I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) |
                   GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
 
-       /* WaDisableKillLogic:bxt,skl,kbl,cfl */
-       I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
-                  ECOCHK_DIS_TLB);
+       /* WaDisableKillLogic:bxt,skl,kbl */
+       if (!IS_COFFEELAKE(dev_priv))
+               I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+                          ECOCHK_DIS_TLB);
 
        /* WaClearFlowControlGpgpuContextSave:skl,bxt,kbl,glk,cfl */
        /* WaDisablePartialInstShootdown:skl,bxt,kbl,glk,cfl */
@@ -894,10 +895,9 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
        WA_SET_BIT_MASKED(HDC_CHICKEN0,
                          HDC_FORCE_NON_COHERENT);
 
-       /* WaDisableHDCInvalidation:skl,bxt,kbl */
-       if (!IS_COFFEELAKE(dev_priv))
-               I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
-                          BDW_DISABLE_HDC_INVALIDATION);
+       /* WaDisableHDCInvalidation:skl,bxt,kbl,cfl */
+       I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+                  BDW_DISABLE_HDC_INVALIDATION);
 
        /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl,cfl */
        if (IS_SKYLAKE(dev_priv) ||
index 03347c6ae5993a8312c539c3b88fa0381f7a9904..0c4cde6b2e6fc7aad314e7c7f329f51f0937a87d 100644 (file)
@@ -535,13 +535,14 @@ static void intel_fbdev_destroy(struct intel_fbdev *ifbdev)
 
        drm_fb_helper_fini(&ifbdev->helper);
 
-       if (ifbdev->fb) {
+       if (ifbdev->vma) {
                mutex_lock(&ifbdev->helper.dev->struct_mutex);
                intel_unpin_fb_vma(ifbdev->vma);
                mutex_unlock(&ifbdev->helper.dev->struct_mutex);
+       }
 
+       if (ifbdev->fb)
                drm_framebuffer_remove(&ifbdev->fb->base);
-       }
 
        kfree(ifbdev);
 }
@@ -765,7 +766,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous
        struct intel_fbdev *ifbdev = dev_priv->fbdev;
        struct fb_info *info;
 
-       if (!ifbdev || !ifbdev->fb)
+       if (!ifbdev || !ifbdev->vma)
                return;
 
        info = ifbdev->helper.fbdev;
@@ -812,7 +813,7 @@ void intel_fbdev_output_poll_changed(struct drm_device *dev)
 {
        struct intel_fbdev *ifbdev = to_i915(dev)->fbdev;
 
-       if (ifbdev && ifbdev->fb)
+       if (ifbdev && ifbdev->vma)
                drm_fb_helper_hotplug_event(&ifbdev->helper);
 }
 
@@ -824,7 +825,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
                return;
 
        intel_fbdev_sync(ifbdev);
-       if (!ifbdev->fb)
+       if (!ifbdev->vma)
                return;
 
        if (drm_fb_helper_restore_fbdev_mode_unlocked(&ifbdev->helper) == 0)
index d15cc9d3a5cd1960f55a74ac0424c3e77f503f25..89dc25a5a53b87182967d9603a842d5aa2300c3e 100644 (file)
@@ -246,9 +246,9 @@ static int igt_dmabuf_export_vmap(void *arg)
        i915_gem_object_put(obj);
 
        ptr = dma_buf_vmap(dmabuf);
-       if (IS_ERR(ptr)) {
-               err = PTR_ERR(ptr);
-               pr_err("dma_buf_vmap failed with err=%d\n", err);
+       if (!ptr) {
+               pr_err("dma_buf_vmap failed\n");
+               err = -ENOMEM;
                goto out;
        }
 
index bf2e5be1ab30df7699b99512b3e67048036726f9..e37b55a23a659d407f958744bd3220e921f68ce8 100644 (file)
@@ -1,4 +1,5 @@
-mediatek-drm-y := mtk_disp_ovl.o \
+mediatek-drm-y := mtk_disp_color.o \
+                 mtk_disp_ovl.o \
                  mtk_disp_rdma.o \
                  mtk_drm_crtc.o \
                  mtk_drm_ddp.o \
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_color.c b/drivers/gpu/drm/mediatek/mtk_disp_color.c
new file mode 100644 (file)
index 0000000..ef79a6d
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#include "mtk_drm_crtc.h"
+#include "mtk_drm_ddp_comp.h"
+
+#define DISP_COLOR_CFG_MAIN                    0x0400
+#define DISP_COLOR_START_MT2701                        0x0f00
+#define DISP_COLOR_START_MT8173                        0x0c00
+#define DISP_COLOR_START(comp)                 ((comp)->data->color_offset)
+#define DISP_COLOR_WIDTH(comp)                 (DISP_COLOR_START(comp) + 0x50)
+#define DISP_COLOR_HEIGHT(comp)                        (DISP_COLOR_START(comp) + 0x54)
+
+#define COLOR_BYPASS_ALL                       BIT(7)
+#define COLOR_SEQ_SEL                          BIT(13)
+
+struct mtk_disp_color_data {
+       unsigned int color_offset;
+};
+
+/**
+ * struct mtk_disp_color - DISP_COLOR driver structure
+ * @ddp_comp - structure containing type enum and hardware resources
+ * @crtc - associated crtc to report irq events to
+ */
+struct mtk_disp_color {
+       struct mtk_ddp_comp                     ddp_comp;
+       struct drm_crtc                         *crtc;
+       const struct mtk_disp_color_data        *data;
+};
+
+static inline struct mtk_disp_color *comp_to_color(struct mtk_ddp_comp *comp)
+{
+       return container_of(comp, struct mtk_disp_color, ddp_comp);
+}
+
+static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
+                            unsigned int h, unsigned int vrefresh,
+                            unsigned int bpc)
+{
+       struct mtk_disp_color *color = comp_to_color(comp);
+
+       writel(w, comp->regs + DISP_COLOR_WIDTH(color));
+       writel(h, comp->regs + DISP_COLOR_HEIGHT(color));
+}
+
+static void mtk_color_start(struct mtk_ddp_comp *comp)
+{
+       struct mtk_disp_color *color = comp_to_color(comp);
+
+       writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
+              comp->regs + DISP_COLOR_CFG_MAIN);
+       writel(0x1, comp->regs + DISP_COLOR_START(color));
+}
+
+static const struct mtk_ddp_comp_funcs mtk_disp_color_funcs = {
+       .config = mtk_color_config,
+       .start = mtk_color_start,
+};
+
+static int mtk_disp_color_bind(struct device *dev, struct device *master,
+                              void *data)
+{
+       struct mtk_disp_color *priv = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+       int ret;
+
+       ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
+       if (ret < 0) {
+               dev_err(dev, "Failed to register component %s: %d\n",
+                       dev->of_node->full_name, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void mtk_disp_color_unbind(struct device *dev, struct device *master,
+                                 void *data)
+{
+       struct mtk_disp_color *priv = dev_get_drvdata(dev);
+       struct drm_device *drm_dev = data;
+
+       mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
+}
+
+static const struct component_ops mtk_disp_color_component_ops = {
+       .bind   = mtk_disp_color_bind,
+       .unbind = mtk_disp_color_unbind,
+};
+
+static int mtk_disp_color_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct mtk_disp_color *priv;
+       int comp_id;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_COLOR);
+       if (comp_id < 0) {
+               dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
+               return comp_id;
+       }
+
+       ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
+                               &mtk_disp_color_funcs);
+       if (ret) {
+               dev_err(dev, "Failed to initialize component: %d\n", ret);
+               return ret;
+       }
+
+       priv->data = of_device_get_match_data(dev);
+
+       platform_set_drvdata(pdev, priv);
+
+       ret = component_add(dev, &mtk_disp_color_component_ops);
+       if (ret)
+               dev_err(dev, "Failed to add component: %d\n", ret);
+
+       return ret;
+}
+
+static int mtk_disp_color_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &mtk_disp_color_component_ops);
+
+       return 0;
+}
+
+static const struct mtk_disp_color_data mt2701_color_driver_data = {
+       .color_offset = DISP_COLOR_START_MT2701,
+};
+
+static const struct mtk_disp_color_data mt8173_color_driver_data = {
+       .color_offset = DISP_COLOR_START_MT8173,
+};
+
+static const struct of_device_id mtk_disp_color_driver_dt_match[] = {
+       { .compatible = "mediatek,mt2701-disp-color",
+         .data = &mt2701_color_driver_data},
+       { .compatible = "mediatek,mt8173-disp-color",
+         .data = &mt8173_color_driver_data},
+       {},
+};
+MODULE_DEVICE_TABLE(of, mtk_disp_color_driver_dt_match);
+
+struct platform_driver mtk_disp_color_driver = {
+       .probe          = mtk_disp_color_probe,
+       .remove         = mtk_disp_color_remove,
+       .driver         = {
+               .name   = "mediatek-disp-color",
+               .owner  = THIS_MODULE,
+               .of_match_table = mtk_disp_color_driver_dt_match,
+       },
+};
index a14d7d64d7b1112224bad99d55e647331291934b..35bc5babdbf70d5263e06c9d5f0cb0ebd5715e3c 100644 (file)
 #define        OVL_RDMA_MEM_GMC        0x40402020
 
 #define OVL_CON_BYTE_SWAP      BIT(24)
+#define OVL_CON_MTX_YUV_TO_RGB (6 << 16)
 #define OVL_CON_CLRFMT_RGB     (1 << 12)
 #define OVL_CON_CLRFMT_RGBA8888        (2 << 12)
 #define OVL_CON_CLRFMT_ARGB8888        (3 << 12)
+#define OVL_CON_CLRFMT_UYVY    (4 << 12)
+#define OVL_CON_CLRFMT_YUYV    (5 << 12)
 #define OVL_CON_CLRFMT_RGB565(ovl)     ((ovl)->data->fmt_rgb565_is_0 ? \
                                        0 : OVL_CON_CLRFMT_RGB)
 #define OVL_CON_CLRFMT_RGB888(ovl)     ((ovl)->data->fmt_rgb565_is_0 ? \
@@ -176,6 +179,10 @@ static unsigned int ovl_fmt_convert(struct mtk_disp_ovl *ovl, unsigned int fmt)
        case DRM_FORMAT_XBGR8888:
        case DRM_FORMAT_ABGR8888:
                return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
+       case DRM_FORMAT_UYVY:
+               return OVL_CON_CLRFMT_UYVY | OVL_CON_MTX_YUV_TO_RGB;
+       case DRM_FORMAT_YUYV:
+               return OVL_CON_CLRFMT_YUYV | OVL_CON_MTX_YUV_TO_RGB;
        }
 }
 
index 6582e1f56d37cfb23bcf54a740ddf22a207c7ded..cb32c9369f3a6259914898bcc6817bc9d2fd0e99 100644 (file)
@@ -559,6 +559,8 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
        mtk_crtc->ddp_comp = devm_kmalloc_array(dev, mtk_crtc->ddp_comp_nr,
                                                sizeof(*mtk_crtc->ddp_comp),
                                                GFP_KERNEL);
+       if (!mtk_crtc->ddp_comp)
+               return -ENOMEM;
 
        mtk_crtc->mutex = mtk_disp_mutex_get(priv->mutex_dev, pipe);
        if (IS_ERR(mtk_crtc->mutex)) {
index 8b52416b6e41c6b0576679009a5d89ec9a437364..07d7ea2268eff7b4b3e43cca47fbd889c2d2fa99 100644 (file)
 
 #define DISP_REG_UFO_START                     0x0000
 
-#define DISP_COLOR_CFG_MAIN                    0x0400
-#define DISP_COLOR_START_MT2701                        0x0f00
-#define DISP_COLOR_START_MT8173                        0x0c00
-#define DISP_COLOR_START(comp)                 ((comp)->data->color_offset)
-#define DISP_COLOR_WIDTH(comp)                 (DISP_COLOR_START(comp) + 0x50)
-#define DISP_COLOR_HEIGHT(comp)                        (DISP_COLOR_START(comp) + 0x54)
-
 #define DISP_AAL_EN                            0x0000
 #define DISP_AAL_SIZE                          0x0030
 
@@ -55,9 +48,6 @@
 
 #define LUT_10BIT_MASK                         0x03ff
 
-#define COLOR_BYPASS_ALL                       BIT(7)
-#define COLOR_SEQ_SEL                          BIT(13)
-
 #define OD_RELAYMODE                           BIT(0)
 
 #define UFO_BYPASS                             BIT(2)
 #define DITHER_ADD_LSHIFT_G(x)                 (((x) & 0x7) << 4)
 #define DITHER_ADD_RSHIFT_G(x)                 (((x) & 0x7) << 0)
 
-struct mtk_disp_color_data {
-       unsigned int color_offset;
-};
-
-struct mtk_disp_color {
-       struct mtk_ddp_comp                     ddp_comp;
-       const struct mtk_disp_color_data        *data;
-};
-
-static inline struct mtk_disp_color *comp_to_color(struct mtk_ddp_comp *comp)
-{
-       return container_of(comp, struct mtk_disp_color, ddp_comp);
-}
-
 void mtk_dither_set(struct mtk_ddp_comp *comp, unsigned int bpc,
                    unsigned int CFG)
 {
@@ -119,25 +95,6 @@ void mtk_dither_set(struct mtk_ddp_comp *comp, unsigned int bpc,
        }
 }
 
-static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
-                            unsigned int h, unsigned int vrefresh,
-                            unsigned int bpc)
-{
-       struct mtk_disp_color *color = comp_to_color(comp);
-
-       writel(w, comp->regs + DISP_COLOR_WIDTH(color));
-       writel(h, comp->regs + DISP_COLOR_HEIGHT(color));
-}
-
-static void mtk_color_start(struct mtk_ddp_comp *comp)
-{
-       struct mtk_disp_color *color = comp_to_color(comp);
-
-       writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
-              comp->regs + DISP_COLOR_CFG_MAIN);
-       writel(0x1, comp->regs + DISP_COLOR_START(color));
-}
-
 static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
                          unsigned int h, unsigned int vrefresh,
                          unsigned int bpc)
@@ -229,11 +186,6 @@ static const struct mtk_ddp_comp_funcs ddp_gamma = {
        .stop = mtk_gamma_stop,
 };
 
-static const struct mtk_ddp_comp_funcs ddp_color = {
-       .config = mtk_color_config,
-       .start = mtk_color_start,
-};
-
 static const struct mtk_ddp_comp_funcs ddp_od = {
        .config = mtk_od_config,
        .start = mtk_od_start,
@@ -268,8 +220,8 @@ struct mtk_ddp_comp_match {
 static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
        [DDP_COMPONENT_AAL]     = { MTK_DISP_AAL,       0, &ddp_aal },
        [DDP_COMPONENT_BLS]     = { MTK_DISP_BLS,       0, NULL },
-       [DDP_COMPONENT_COLOR0]  = { MTK_DISP_COLOR,     0, &ddp_color },
-       [DDP_COMPONENT_COLOR1]  = { MTK_DISP_COLOR,     1, &ddp_color },
+       [DDP_COMPONENT_COLOR0]  = { MTK_DISP_COLOR,     0, NULL },
+       [DDP_COMPONENT_COLOR1]  = { MTK_DISP_COLOR,     1, NULL },
        [DDP_COMPONENT_DPI0]    = { MTK_DPI,            0, NULL },
        [DDP_COMPONENT_DSI0]    = { MTK_DSI,            0, NULL },
        [DDP_COMPONENT_DSI1]    = { MTK_DSI,            1, NULL },
@@ -286,22 +238,6 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
        [DDP_COMPONENT_WDMA1]   = { MTK_DISP_WDMA,      1, NULL },
 };
 
-static const struct mtk_disp_color_data mt2701_color_driver_data = {
-       .color_offset = DISP_COLOR_START_MT2701,
-};
-
-static const struct mtk_disp_color_data mt8173_color_driver_data = {
-       .color_offset = DISP_COLOR_START_MT8173,
-};
-
-static const struct of_device_id mtk_disp_color_driver_dt_match[] = {
-       { .compatible = "mediatek,mt2701-disp-color",
-         .data = &mt2701_color_driver_data},
-       { .compatible = "mediatek,mt8173-disp-color",
-         .data = &mt8173_color_driver_data},
-       {},
-};
-
 int mtk_ddp_comp_get_id(struct device_node *node,
                        enum mtk_ddp_comp_type comp_type)
 {
@@ -324,23 +260,11 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
        enum mtk_ddp_comp_type type;
        struct device_node *larb_node;
        struct platform_device *larb_pdev;
-       const struct of_device_id *match;
-       struct mtk_disp_color *color;
 
        if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
                return -EINVAL;
 
        type = mtk_ddp_matches[comp_id].type;
-       if (type == MTK_DISP_COLOR) {
-               devm_kfree(dev, comp);
-               color = devm_kzalloc(dev, sizeof(*color), GFP_KERNEL);
-               if (!color)
-                       return -ENOMEM;
-
-               match = of_match_node(mtk_disp_color_driver_dt_match, node);
-               color->data = match->data;
-               comp = &color->ddp_comp;
-       }
 
        comp->id = comp_id;
        comp->funcs = funcs ?: mtk_ddp_matches[comp_id].funcs;
index f6c8ec4c7dbcb46705ba87bfd5996776484eed60..41d2cffe953e59f220eb53de2821583d6211e7bc 100644 (file)
@@ -439,11 +439,12 @@ static int mtk_drm_probe(struct platform_device *pdev)
                private->comp_node[comp_id] = of_node_get(node);
 
                /*
-                * Currently only the OVL, RDMA, DSI, and DPI blocks have
+                * Currently only the COLOR, OVL, RDMA, DSI, and DPI blocks have
                 * separate component platform drivers and initialize their own
                 * DDP component structure. The others are initialized here.
                 */
-               if (comp_type == MTK_DISP_OVL ||
+               if (comp_type == MTK_DISP_COLOR ||
+                   comp_type == MTK_DISP_OVL ||
                    comp_type == MTK_DISP_RDMA ||
                    comp_type == MTK_DSI ||
                    comp_type == MTK_DPI) {
@@ -566,6 +567,7 @@ static struct platform_driver mtk_drm_platform_driver = {
 
 static struct platform_driver * const mtk_drm_drivers[] = {
        &mtk_ddp_driver,
+       &mtk_disp_color_driver,
        &mtk_disp_ovl_driver,
        &mtk_disp_rdma_driver,
        &mtk_dpi_driver,
@@ -576,33 +578,14 @@ static struct platform_driver * const mtk_drm_drivers[] = {
 
 static int __init mtk_drm_init(void)
 {
-       int ret;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(mtk_drm_drivers); i++) {
-               ret = platform_driver_register(mtk_drm_drivers[i]);
-               if (ret < 0) {
-                       pr_err("Failed to register %s driver: %d\n",
-                              mtk_drm_drivers[i]->driver.name, ret);
-                       goto err;
-               }
-       }
-
-       return 0;
-
-err:
-       while (--i >= 0)
-               platform_driver_unregister(mtk_drm_drivers[i]);
-
-       return ret;
+       return platform_register_drivers(mtk_drm_drivers,
+                                        ARRAY_SIZE(mtk_drm_drivers));
 }
 
 static void __exit mtk_drm_exit(void)
 {
-       int i;
-
-       for (i = ARRAY_SIZE(mtk_drm_drivers) - 1; i >= 0; i--)
-               platform_driver_unregister(mtk_drm_drivers[i]);
+       platform_unregister_drivers(mtk_drm_drivers,
+                                   ARRAY_SIZE(mtk_drm_drivers));
 }
 
 module_init(mtk_drm_init);
index aef8747d810bdbe1bc537aab46ba2fd0a674b1bb..c3378c452c0a03134c1eaa6175921102fc8ab751 100644 (file)
@@ -59,6 +59,7 @@ struct mtk_drm_private {
 };
 
 extern struct platform_driver mtk_ddp_driver;
+extern struct platform_driver mtk_disp_color_driver;
 extern struct platform_driver mtk_disp_ovl_driver;
 extern struct platform_driver mtk_disp_rdma_driver;
 extern struct platform_driver mtk_dpi_driver;
index e405e89ed5e5dcb7b7bbe131a4e5dd19ac66fa4b..1a59b9ab4aa817701938235dd9ac397d75206098 100644 (file)
@@ -28,6 +28,8 @@ static const u32 formats[] = {
        DRM_FORMAT_XRGB8888,
        DRM_FORMAT_ARGB8888,
        DRM_FORMAT_RGB565,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_YUYV,
 };
 
 static void mtk_plane_reset(struct drm_plane *plane)
index b5cc6e12334cf96e8faacc01a1a8fb5dcec48202..97253c8f813b0ea026f03a686215c0783ec35b7c 100644 (file)
@@ -930,7 +930,7 @@ static u32 mtk_dsi_recv_cnt(u8 type, u8 *read_data)
                DRM_INFO("type is 0x02, try again\n");
                break;
        default:
-               DRM_INFO("type(0x%x) cannot be non-recognite\n", type);
+               DRM_INFO("type(0x%x) not recognized\n", type);
                break;
        }
 
index 0a4ffd7241468dcbd064fa3a210f17094d10697b..71eb4fbbfc85fd74e260fccc1ecdb96bacfc6644 100644 (file)
@@ -1778,33 +1778,14 @@ static struct platform_driver * const mtk_hdmi_drivers[] = {
 
 static int __init mtk_hdmitx_init(void)
 {
-       int ret;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(mtk_hdmi_drivers); i++) {
-               ret = platform_driver_register(mtk_hdmi_drivers[i]);
-               if (ret < 0) {
-                       pr_err("Failed to register %s driver: %d\n",
-                              mtk_hdmi_drivers[i]->driver.name, ret);
-                       goto err;
-               }
-       }
-
-       return 0;
-
-err:
-       while (--i >= 0)
-               platform_driver_unregister(mtk_hdmi_drivers[i]);
-
-       return ret;
+       return platform_register_drivers(mtk_hdmi_drivers,
+                                        ARRAY_SIZE(mtk_hdmi_drivers));
 }
 
 static void __exit mtk_hdmitx_exit(void)
 {
-       int i;
-
-       for (i = ARRAY_SIZE(mtk_hdmi_drivers) - 1; i >= 0; i--)
-               platform_driver_unregister(mtk_hdmi_drivers[i]);
+       platform_unregister_drivers(mtk_hdmi_drivers,
+                                   ARRAY_SIZE(mtk_hdmi_drivers));
 }
 
 module_init(mtk_hdmitx_init);
index fa4f8f008e4dc3d93a799e6925c430e3d2e999ea..e67ed383e11b124fc34a63b9d27d42071f15365d 100644 (file)
@@ -31,6 +31,7 @@
 #include "radeon_asic.h"
 #include "atom.h"
 #include <linux/backlight.h>
+#include <linux/dmi.h>
 
 extern int atom_debug;
 
@@ -2184,9 +2185,17 @@ int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder, int fe_idx)
                goto assigned;
        }
 
-       /* on DCE32 and encoder can driver any block so just crtc id */
+       /*
+        * On DCE32 any encoder can drive any block so usually just use crtc id,
+        * but Apple thinks different at least on iMac10,1, so there use linkb,
+        * otherwise the internal eDP panel will stay dark.
+        */
        if (ASIC_IS_DCE32(rdev)) {
-               enc_idx = radeon_crtc->crtc_id;
+               if (dmi_match(DMI_PRODUCT_NAME, "iMac10,1"))
+                       enc_idx = (dig->linkb) ? 1 : 0;
+               else
+                       enc_idx = radeon_crtc->crtc_id;
+
                goto assigned;
        }
 
index 14fa1f8351e8df22ab30560fbb6a1906841ba43d..9b0b0588bbedbc8becb6d94a6e38c177c3615092 100644 (file)
@@ -1195,7 +1195,7 @@ static int cdn_dp_probe(struct platform_device *pdev)
                        continue;
 
                port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
-               if (!dp)
+               if (!port)
                        return -ENOMEM;
 
                port->extcon = extcon;
index 47905faf55863f93b5768756f8f667ddcec5226a..c7e96b82cf6397b95191a34beee6380d39183779 100644 (file)
@@ -45,13 +45,13 @@ struct rockchip_crtc_state {
  *
  * @crtc: array of enabled CRTCs, used to map from "pipe" to drm_crtc.
  * @num_pipe: number of pipes for this device.
+ * @mm_lock: protect drm_mm on multi-threads.
  */
 struct rockchip_drm_private {
        struct drm_fb_helper fbdev_helper;
        struct drm_gem_object *fbdev_bo;
        struct drm_atomic_state *state;
        struct iommu_domain *domain;
-       /* protect drm_mm on multi-threads */
        struct mutex mm_lock;
        struct drm_mm mm;
        struct list_head psr_list;
index df9e57064f1983ae192413dfefb20fa3c9a8319f..b74ac717e56a502a647bf3a601c1166e7787ffa2 100644 (file)
@@ -29,12 +29,11 @@ static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
        ssize_t ret;
 
        mutex_lock(&private->mm_lock);
-
        ret = drm_mm_insert_node_generic(&private->mm, &rk_obj->mm,
                                         rk_obj->base.size, PAGE_SIZE,
                                         0, 0);
-
        mutex_unlock(&private->mm_lock);
+
        if (ret < 0) {
                DRM_ERROR("out of I/O virtual memory: %zd\n", ret);
                return ret;
@@ -56,7 +55,9 @@ static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
        return 0;
 
 err_remove_node:
+       mutex_lock(&private->mm_lock);
        drm_mm_remove_node(&rk_obj->mm);
+       mutex_unlock(&private->mm_lock);
 
        return ret;
 }
index e83a279f1217e7b9959ed7caa7e4a8be13309a1d..5a2d71729b9ace7a67e8216f7d5ba61fddb471d7 100644 (file)
@@ -155,6 +155,10 @@ config MTD_BCM47XX_PARTS
          This provides partitions parser for devices based on BCM47xx
          boards.
 
+menu "Partition parsers"
+source "drivers/mtd/parsers/Kconfig"
+endmenu
+
 comment "User Modules And Translation Layers"
 
 #
index 99bb9a1f6e16fc324b7b0a794e06f6abbeb39575..151d60df303acdbab6db611440be753ab4987b2a 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_AFS_PARTS)   += afs.o
 obj-$(CONFIG_MTD_AR7_PARTS)    += ar7part.o
 obj-$(CONFIG_MTD_BCM63XX_PARTS)        += bcm63xxpart.o
 obj-$(CONFIG_MTD_BCM47XX_PARTS)        += bcm47xxpart.o
+obj-y                          += parsers/
 
 # 'Users' - code which presents functionality to userspace.
 obj-$(CONFIG_MTD_BLKDEVS)      += mtd_blkdevs.o
index d10fa6c8f074648e0a7ec5e05562e15f4695b849..fe2581d9d882f2f480f4f5ec4b458cb3d67268e9 100644 (file)
@@ -43,7 +43,8 @@
 #define ML_MAGIC2                      0x26594131
 #define TRX_MAGIC                      0x30524448
 #define SHSQ_MAGIC                     0x71736873      /* shsq (weird ZTE H218N endianness) */
-#define UBI_EC_MAGIC                   0x23494255      /* UBI# */
+
+static const char * const trx_types[] = { "trx", NULL };
 
 struct trx_header {
        uint32_t magic;
@@ -62,89 +63,6 @@ static void bcm47xxpart_add_part(struct mtd_partition *part, const char *name,
        part->mask_flags = mask_flags;
 }
 
-static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master,
-                                                 size_t offset)
-{
-       uint32_t buf;
-       size_t bytes_read;
-       int err;
-
-       err  = mtd_read(master, offset, sizeof(buf), &bytes_read,
-                       (uint8_t *)&buf);
-       if (err && !mtd_is_bitflip(err)) {
-               pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
-                       offset, err);
-               goto out_default;
-       }
-
-       if (buf == UBI_EC_MAGIC)
-               return "ubi";
-
-out_default:
-       return "rootfs";
-}
-
-static int bcm47xxpart_parse_trx(struct mtd_info *master,
-                                struct mtd_partition *trx,
-                                struct mtd_partition *parts,
-                                size_t parts_len)
-{
-       struct trx_header header;
-       size_t bytes_read;
-       int curr_part = 0;
-       int i, err;
-
-       if (parts_len < 3) {
-               pr_warn("No enough space to add TRX partitions!\n");
-               return -ENOMEM;
-       }
-
-       err = mtd_read(master, trx->offset, sizeof(header), &bytes_read,
-                      (uint8_t *)&header);
-       if (err && !mtd_is_bitflip(err)) {
-               pr_err("mtd_read error while reading TRX header: %d\n", err);
-               return err;
-       }
-
-       i = 0;
-
-       /* We have LZMA loader if offset[2] points to sth */
-       if (header.offset[2]) {
-               bcm47xxpart_add_part(&parts[curr_part++], "loader",
-                                    trx->offset + header.offset[i], 0);
-               i++;
-       }
-
-       if (header.offset[i]) {
-               bcm47xxpart_add_part(&parts[curr_part++], "linux",
-                                    trx->offset + header.offset[i], 0);
-               i++;
-       }
-
-       if (header.offset[i]) {
-               size_t offset = trx->offset + header.offset[i];
-               const char *name = bcm47xxpart_trx_data_part_name(master,
-                                                                 offset);
-
-               bcm47xxpart_add_part(&parts[curr_part++], name, offset, 0);
-               i++;
-       }
-
-       /*
-        * Assume that every partition ends at the beginning of the one it is
-        * followed by.
-        */
-       for (i = 0; i < curr_part; i++) {
-               u64 next_part_offset = (i < curr_part - 1) ?
-                                       parts[i + 1].offset :
-                                       trx->offset + trx->size;
-
-               parts[i].size = next_part_offset - parts[i].offset;
-       }
-
-       return curr_part;
-}
-
 /**
  * bcm47xxpart_bootpartition - gets index of TRX partition used by bootloader
  *
@@ -362,17 +280,10 @@ static int bcm47xxpart_parse(struct mtd_info *master,
        for (i = 0; i < trx_num; i++) {
                struct mtd_partition *trx = &parts[trx_parts[i]];
 
-               if (i == bcm47xxpart_bootpartition()) {
-                       int num_parts;
-
-                       num_parts = bcm47xxpart_parse_trx(master, trx,
-                                                         parts + curr_part,
-                                                         BCM47XXPART_MAX_PARTS - curr_part);
-                       if (num_parts > 0)
-                               curr_part += num_parts;
-               } else {
+               if (i == bcm47xxpart_bootpartition())
+                       trx->types = trx_types;
+               else
                        trx->name = "failsafe";
-               }
        }
 
        *pparts = parts;
index 94d3eb42c4d5f2c55dc6db811ae3a5280e58f95e..7d342965f392232d89919a0182982e7764982b82 100644 (file)
@@ -666,7 +666,7 @@ cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs,
        size_t   totlen = 0, thislen;
        int      ret = 0;
        size_t   buflen = 0;
-       static char *buffer;
+       char *buffer;
 
        if (!ECCBUF_SIZE) {
                /* We should fall back to a general writev implementation.
index 58329d2dacd1f74da67e9c45e001b01de41d4e94..6def5445e03e185cdbb92a997bc0a0db95a37b41 100644 (file)
@@ -95,6 +95,16 @@ config MTD_M25P80
          if you want to specify device partitioning or to use a device which
          doesn't support the JEDEC ID instruction.
 
+config MTD_MCHP23K256
+       tristate "Microchip 23K256 SRAM"
+       depends on SPI_MASTER
+       help
+         This enables access to Microchip 23K256 SRAM chips, using SPI.
+
+         Set up your spi devices with the right board-specific
+         platform data, or a device tree description if you want to
+         specify device partitioning
+
 config MTD_SPEAR_SMI
        tristate "SPEAR MTD NOR Support through SMI controller"
        depends on PLAT_SPEAR
index 7912d3a0ee343b045daf1dbdf9ae93061afad4eb..f0f767624cc68d987e968e826b1c8d16d820e078 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_MTD_LART)                += lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)    += block2mtd.o
 obj-$(CONFIG_MTD_DATAFLASH)    += mtd_dataflash.o
 obj-$(CONFIG_MTD_M25P80)       += m25p80.o
+obj-$(CONFIG_MTD_MCHP23K256)   += mchp23k256.o
 obj-$(CONFIG_MTD_SPEAR_SMI)    += spear_smi.o
 obj-$(CONFIG_MTD_SST25L)       += sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)        += bcm47xxsflash.o
index c4df3b1bded0bac4acf44bbbe8bb4430c073f77a..00eea6fd379cc68d51dbe197eec9b8b310341fdb 100644 (file)
@@ -78,11 +78,17 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 {
        struct m25p *flash = nor->priv;
        struct spi_device *spi = flash->spi;
-       struct spi_transfer t[2] = {};
+       unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
+       struct spi_transfer t[3] = {};
        struct spi_message m;
        int cmd_sz = m25p_cmdsz(nor);
        ssize_t ret;
 
+       /* get transfer protocols. */
+       inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
+       addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
+       data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
        spi_message_init(&m);
 
        if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
@@ -92,12 +98,27 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
        m25p_addr2cmd(nor, to, flash->command);
 
        t[0].tx_buf = flash->command;
+       t[0].tx_nbits = inst_nbits;
        t[0].len = cmd_sz;
        spi_message_add_tail(&t[0], &m);
 
-       t[1].tx_buf = buf;
-       t[1].len = len;
-       spi_message_add_tail(&t[1], &m);
+       /* split the op code and address bytes into two transfers if needed. */
+       data_idx = 1;
+       if (addr_nbits != inst_nbits) {
+               t[0].len = 1;
+
+               t[1].tx_buf = &flash->command[1];
+               t[1].tx_nbits = addr_nbits;
+               t[1].len = cmd_sz - 1;
+               spi_message_add_tail(&t[1], &m);
+
+               data_idx = 2;
+       }
+
+       t[data_idx].tx_buf = buf;
+       t[data_idx].tx_nbits = data_nbits;
+       t[data_idx].len = len;
+       spi_message_add_tail(&t[data_idx], &m);
 
        ret = spi_sync(spi, &m);
        if (ret)
@@ -109,18 +130,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
        return ret;
 }
 
-static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
-{
-       switch (nor->flash_read) {
-       case SPI_NOR_DUAL:
-               return 2;
-       case SPI_NOR_QUAD:
-               return 4;
-       default:
-               return 0;
-       }
-}
-
 /*
  * Read an address range from the nor chip.  The address range
  * may be any size provided it is within the physical boundaries.
@@ -130,13 +139,20 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 {
        struct m25p *flash = nor->priv;
        struct spi_device *spi = flash->spi;
-       struct spi_transfer t[2];
+       unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
+       struct spi_transfer t[3];
        struct spi_message m;
        unsigned int dummy = nor->read_dummy;
        ssize_t ret;
+       int cmd_sz;
+
+       /* get transfer protocols. */
+       inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);
+       addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);
+       data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);
 
        /* convert the dummy cycles to the number of bytes */
-       dummy /= 8;
+       dummy = (dummy * addr_nbits) / 8;
 
        if (spi_flash_read_supported(spi)) {
                struct spi_flash_read_message msg;
@@ -149,10 +165,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t 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);
+               msg.opcode_nbits = inst_nbits;
+               msg.addr_nbits = addr_nbits;
+               msg.data_nbits = data_nbits;
 
                ret = spi_flash_read(spi, &msg);
                if (ret < 0)
@@ -167,20 +182,45 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
        m25p_addr2cmd(nor, from, flash->command);
 
        t[0].tx_buf = flash->command;
+       t[0].tx_nbits = inst_nbits;
        t[0].len = m25p_cmdsz(nor) + dummy;
        spi_message_add_tail(&t[0], &m);
 
-       t[1].rx_buf = buf;
-       t[1].rx_nbits = m25p80_rx_nbits(nor);
-       t[1].len = min3(len, spi_max_transfer_size(spi),
-                       spi_max_message_size(spi) - t[0].len);
-       spi_message_add_tail(&t[1], &m);
+       /*
+        * Set all dummy/mode cycle bits to avoid sending some manufacturer
+        * specific pattern, which might make the memory enter its Continuous
+        * Read mode by mistake.
+        * Based on the different mode cycle bit patterns listed and described
+        * in the JESD216B specification, the 0xff value works for all memories
+        * and all manufacturers.
+        */
+       cmd_sz = t[0].len;
+       memset(flash->command + cmd_sz - dummy, 0xff, dummy);
+
+       /* split the op code and address bytes into two transfers if needed. */
+       data_idx = 1;
+       if (addr_nbits != inst_nbits) {
+               t[0].len = 1;
+
+               t[1].tx_buf = &flash->command[1];
+               t[1].tx_nbits = addr_nbits;
+               t[1].len = cmd_sz - 1;
+               spi_message_add_tail(&t[1], &m);
+
+               data_idx = 2;
+       }
+
+       t[data_idx].rx_buf = buf;
+       t[data_idx].rx_nbits = data_nbits;
+       t[data_idx].len = min3(len, spi_max_transfer_size(spi),
+                              spi_max_message_size(spi) - cmd_sz);
+       spi_message_add_tail(&t[data_idx], &m);
 
        ret = spi_sync(spi, &m);
        if (ret)
                return ret;
 
-       ret = m.actual_length - m25p_cmdsz(nor) - dummy;
+       ret = m.actual_length - cmd_sz;
        if (ret < 0)
                return -EIO;
        return ret;
@@ -196,7 +236,11 @@ static int m25p_probe(struct spi_device *spi)
        struct flash_platform_data      *data;
        struct m25p *flash;
        struct spi_nor *nor;
-       enum read_mode mode = SPI_NOR_NORMAL;
+       struct spi_nor_hwcaps hwcaps = {
+               .mask = SNOR_HWCAPS_READ |
+                       SNOR_HWCAPS_READ_FAST |
+                       SNOR_HWCAPS_PP,
+       };
        char *flash_name;
        int ret;
 
@@ -221,10 +265,19 @@ static int m25p_probe(struct spi_device *spi)
        spi_set_drvdata(spi, flash);
        flash->spi = spi;
 
-       if (spi->mode & SPI_RX_QUAD)
-               mode = SPI_NOR_QUAD;
-       else if (spi->mode & SPI_RX_DUAL)
-               mode = SPI_NOR_DUAL;
+       if (spi->mode & SPI_RX_QUAD) {
+               hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+
+               if (spi->mode & SPI_TX_QUAD)
+                       hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
+                                       SNOR_HWCAPS_PP_1_1_4 |
+                                       SNOR_HWCAPS_PP_1_4_4);
+       } else if (spi->mode & SPI_RX_DUAL) {
+               hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+
+               if (spi->mode & SPI_TX_DUAL)
+                       hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
+       }
 
        if (data && data->name)
                nor->mtd.name = data->name;
@@ -241,7 +294,7 @@ static int m25p_probe(struct spi_device *spi)
        else
                flash_name = spi->modalias;
 
-       ret = spi_nor_scan(nor, flash_name, mode);
+       ret = spi_nor_scan(nor, flash_name, &hwcaps);
        if (ret)
                return ret;
 
diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c
new file mode 100644 (file)
index 0000000..8956b7d
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * mchp23k256.c
+ *
+ * Driver for Microchip 23k256 SPI RAM chips
+ *
+ * Copyright © 2016 Andrew Lunn <andrew@lunn.ch>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/spi/flash.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+
+#define MAX_CMD_SIZE           4
+
+struct mchp23_caps {
+       u8 addr_width;
+       unsigned int size;
+};
+
+struct mchp23k256_flash {
+       struct spi_device       *spi;
+       struct mutex            lock;
+       struct mtd_info         mtd;
+       const struct mchp23_caps        *caps;
+};
+
+#define MCHP23K256_CMD_WRITE_STATUS    0x01
+#define MCHP23K256_CMD_WRITE           0x02
+#define MCHP23K256_CMD_READ            0x03
+#define MCHP23K256_MODE_SEQ            BIT(6)
+
+#define to_mchp23k256_flash(x) container_of(x, struct mchp23k256_flash, mtd)
+
+static void mchp23k256_addr2cmd(struct mchp23k256_flash *flash,
+                               unsigned int addr, u8 *cmd)
+{
+       int i;
+
+       /*
+        * Address is sent in big endian (MSB first) and we skip
+        * the first entry of the cmd array which contains the cmd
+        * opcode.
+        */
+       for (i = flash->caps->addr_width; i > 0; i--, addr >>= 8)
+               cmd[i] = addr;
+}
+
+static int mchp23k256_cmdsz(struct mchp23k256_flash *flash)
+{
+       return 1 + flash->caps->addr_width;
+}
+
+static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len,
+                           size_t *retlen, const unsigned char *buf)
+{
+       struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd);
+       struct spi_transfer transfer[2] = {};
+       struct spi_message message;
+       unsigned char command[MAX_CMD_SIZE];
+
+       spi_message_init(&message);
+
+       command[0] = MCHP23K256_CMD_WRITE;
+       mchp23k256_addr2cmd(flash, to, command);
+
+       transfer[0].tx_buf = command;
+       transfer[0].len = mchp23k256_cmdsz(flash);
+       spi_message_add_tail(&transfer[0], &message);
+
+       transfer[1].tx_buf = buf;
+       transfer[1].len = len;
+       spi_message_add_tail(&transfer[1], &message);
+
+       mutex_lock(&flash->lock);
+
+       spi_sync(flash->spi, &message);
+
+       if (retlen && message.actual_length > sizeof(command))
+               *retlen += message.actual_length - sizeof(command);
+
+       mutex_unlock(&flash->lock);
+       return 0;
+}
+
+static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len,
+                          size_t *retlen, unsigned char *buf)
+{
+       struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd);
+       struct spi_transfer transfer[2] = {};
+       struct spi_message message;
+       unsigned char command[MAX_CMD_SIZE];
+
+       spi_message_init(&message);
+
+       memset(&transfer, 0, sizeof(transfer));
+       command[0] = MCHP23K256_CMD_READ;
+       mchp23k256_addr2cmd(flash, from, command);
+
+       transfer[0].tx_buf = command;
+       transfer[0].len = mchp23k256_cmdsz(flash);
+       spi_message_add_tail(&transfer[0], &message);
+
+       transfer[1].rx_buf = buf;
+       transfer[1].len = len;
+       spi_message_add_tail(&transfer[1], &message);
+
+       mutex_lock(&flash->lock);
+
+       spi_sync(flash->spi, &message);
+
+       if (retlen && message.actual_length > sizeof(command))
+               *retlen += message.actual_length - sizeof(command);
+
+       mutex_unlock(&flash->lock);
+       return 0;
+}
+
+/*
+ * Set the device into sequential mode. This allows read/writes to the
+ * entire SRAM in a single operation
+ */
+static int mchp23k256_set_mode(struct spi_device *spi)
+{
+       struct spi_transfer transfer = {};
+       struct spi_message message;
+       unsigned char command[2];
+
+       spi_message_init(&message);
+
+       command[0] = MCHP23K256_CMD_WRITE_STATUS;
+       command[1] = MCHP23K256_MODE_SEQ;
+
+       transfer.tx_buf = command;
+       transfer.len = sizeof(command);
+       spi_message_add_tail(&transfer, &message);
+
+       return spi_sync(spi, &message);
+}
+
+static const struct mchp23_caps mchp23k256_caps = {
+       .size = SZ_32K,
+       .addr_width = 2,
+};
+
+static const struct mchp23_caps mchp23lcv1024_caps = {
+       .size = SZ_128K,
+       .addr_width = 3,
+};
+
+static int mchp23k256_probe(struct spi_device *spi)
+{
+       struct mchp23k256_flash *flash;
+       struct flash_platform_data *data;
+       int err;
+
+       flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
+       if (!flash)
+               return -ENOMEM;
+
+       flash->spi = spi;
+       mutex_init(&flash->lock);
+       spi_set_drvdata(spi, flash);
+
+       err = mchp23k256_set_mode(spi);
+       if (err)
+               return err;
+
+       data = dev_get_platdata(&spi->dev);
+
+       flash->caps = of_device_get_match_data(&spi->dev);
+       if (!flash->caps)
+               flash->caps = &mchp23k256_caps;
+
+       mtd_set_of_node(&flash->mtd, spi->dev.of_node);
+       flash->mtd.dev.parent   = &spi->dev;
+       flash->mtd.type         = MTD_RAM;
+       flash->mtd.flags        = MTD_CAP_RAM;
+       flash->mtd.writesize    = 1;
+       flash->mtd.size         = flash->caps->size;
+       flash->mtd._read        = mchp23k256_read;
+       flash->mtd._write       = mchp23k256_write;
+
+       err = mtd_device_register(&flash->mtd, data ? data->parts : NULL,
+                                 data ? data->nr_parts : 0);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int mchp23k256_remove(struct spi_device *spi)
+{
+       struct mchp23k256_flash *flash = spi_get_drvdata(spi);
+
+       return mtd_device_unregister(&flash->mtd);
+}
+
+static const struct of_device_id mchp23k256_of_table[] = {
+       {
+               .compatible = "microchip,mchp23k256",
+               .data = &mchp23k256_caps,
+       },
+       {
+               .compatible = "microchip,mchp23lcv1024",
+               .data = &mchp23lcv1024_caps,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, mchp23k256_of_table);
+
+static struct spi_driver mchp23k256_driver = {
+       .driver = {
+               .name   = "mchp23k256",
+               .of_match_table = of_match_ptr(mchp23k256_of_table),
+       },
+       .probe          = mchp23k256_probe,
+       .remove         = mchp23k256_remove,
+};
+
+module_spi_driver(mchp23k256_driver);
+
+MODULE_DESCRIPTION("MTD SPI driver for MCHP23K256 RAM chips");
+MODULE_AUTHOR("Andrew Lunn <andre@lunn.ch>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:mchp23k256");
index f9e9bd1cfaa034a4e79f0d4458ca90d47236a6f3..5dc8bd042cc54b2d07407f123d0ee62bce738594 100644 (file)
 #define OP_WRITE_SECURITY_REVC 0x9A
 #define OP_WRITE_SECURITY      0x9B    /* revision D */
 
+#define CFI_MFR_ATMEL          0x1F
+
+#define DATAFLASH_SHIFT_EXTID  24
+#define DATAFLASH_SHIFT_ID     40
 
 struct dataflash {
-       uint8_t                 command[4];
+       u8                      command[4];
        char                    name[24];
 
        unsigned short          page_offset;    /* offset in flash address */
@@ -129,8 +133,7 @@ static int dataflash_waitready(struct spi_device *spi)
        for (;;) {
                status = dataflash_status(spi);
                if (status < 0) {
-                       pr_debug("%s: status %d?\n",
-                                       dev_name(&spi->dev), status);
+                       dev_dbg(&spi->dev, "status %d?\n", status);
                        status = 0;
                }
 
@@ -153,12 +156,11 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
        struct spi_transfer     x = { };
        struct spi_message      msg;
        unsigned                blocksize = priv->page_size << 3;
-       uint8_t                 *command;
-       uint32_t                rem;
+       u8                      *command;
+       u32                     rem;
 
-       pr_debug("%s: erase addr=0x%llx len 0x%llx\n",
-             dev_name(&spi->dev), (long long)instr->addr,
-             (long long)instr->len);
+       dev_dbg(&spi->dev, "erase addr=0x%llx len 0x%llx\n",
+               (long long)instr->addr, (long long)instr->len);
 
        div_u64_rem(instr->len, priv->page_size, &rem);
        if (rem)
@@ -187,11 +189,11 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
                pageaddr = pageaddr << priv->page_offset;
 
                command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
-               command[1] = (uint8_t)(pageaddr >> 16);
-               command[2] = (uint8_t)(pageaddr >> 8);
+               command[1] = (u8)(pageaddr >> 16);
+               command[2] = (u8)(pageaddr >> 8);
                command[3] = 0;
 
-               pr_debug("ERASE %s: (%x) %x %x %x [%i]\n",
+               dev_dbg(&spi->dev, "ERASE %s: (%x) %x %x %x [%i]\n",
                        do_block ? "block" : "page",
                        command[0], command[1], command[2], command[3],
                        pageaddr);
@@ -200,8 +202,8 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
                (void) dataflash_waitready(spi);
 
                if (status < 0) {
-                       printk(KERN_ERR "%s: erase %x, err %d\n",
-                               dev_name(&spi->dev), pageaddr, status);
+                       dev_err(&spi->dev, "erase %x, err %d\n",
+                               pageaddr, status);
                        /* REVISIT:  can retry instr->retries times; or
                         * giveup and instr->fail_addr = instr->addr;
                         */
@@ -239,11 +241,11 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
        struct spi_transfer     x[2] = { };
        struct spi_message      msg;
        unsigned int            addr;
-       uint8_t                 *command;
+       u8                      *command;
        int                     status;
 
-       pr_debug("%s: read 0x%x..0x%x\n", dev_name(&priv->spi->dev),
-                       (unsigned)from, (unsigned)(from + len));
+       dev_dbg(&priv->spi->dev, "read 0x%x..0x%x\n",
+                 (unsigned int)from, (unsigned int)(from + len));
 
        /* Calculate flash page/byte address */
        addr = (((unsigned)from / priv->page_size) << priv->page_offset)
@@ -251,7 +253,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
 
        command = priv->command;
 
-       pr_debug("READ: (%x) %x %x %x\n",
+       dev_dbg(&priv->spi->dev, "READ: (%x) %x %x %x\n",
                command[0], command[1], command[2], command[3]);
 
        spi_message_init(&msg);
@@ -271,9 +273,9 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
         * fewer "don't care" bytes.  Both buffers stay unchanged.
         */
        command[0] = OP_READ_CONTINUOUS;
-       command[1] = (uint8_t)(addr >> 16);
-       command[2] = (uint8_t)(addr >> 8);
-       command[3] = (uint8_t)(addr >> 0);
+       command[1] = (u8)(addr >> 16);
+       command[2] = (u8)(addr >> 8);
+       command[3] = (u8)(addr >> 0);
        /* plus 4 "don't care" bytes */
 
        status = spi_sync(priv->spi, &msg);
@@ -283,8 +285,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
                *retlen = msg.actual_length - 8;
                status = 0;
        } else
-               pr_debug("%s: read %x..%x --> %d\n",
-                       dev_name(&priv->spi->dev),
+               dev_dbg(&priv->spi->dev, "read %x..%x --> %d\n",
                        (unsigned)from, (unsigned)(from + len),
                        status);
        return status;
@@ -308,10 +309,10 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
        size_t                  remaining = len;
        u_char                  *writebuf = (u_char *) buf;
        int                     status = -EINVAL;
-       uint8_t                 *command;
+       u8                      *command;
 
-       pr_debug("%s: write 0x%x..0x%x\n",
-               dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len));
+       dev_dbg(&spi->dev, "write 0x%x..0x%x\n",
+               (unsigned int)to, (unsigned int)(to + len));
 
        spi_message_init(&msg);
 
@@ -328,7 +329,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
 
        mutex_lock(&priv->lock);
        while (remaining > 0) {
-               pr_debug("write @ %i:%i len=%i\n",
+               dev_dbg(&spi->dev, "write @ %i:%i len=%i\n",
                        pageaddr, offset, writelen);
 
                /* REVISIT:
@@ -356,13 +357,13 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
                        command[2] = (addr & 0x0000FF00) >> 8;
                        command[3] = 0;
 
-                       pr_debug("TRANSFER: (%x) %x %x %x\n",
+                       dev_dbg(&spi->dev, "TRANSFER: (%x) %x %x %x\n",
                                command[0], command[1], command[2], command[3]);
 
                        status = spi_sync(spi, &msg);
                        if (status < 0)
-                               pr_debug("%s: xfer %u -> %d\n",
-                                       dev_name(&spi->dev), addr, status);
+                               dev_dbg(&spi->dev, "xfer %u -> %d\n",
+                                       addr, status);
 
                        (void) dataflash_waitready(priv->spi);
                }
@@ -374,7 +375,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
                command[2] = (addr & 0x0000FF00) >> 8;
                command[3] = (addr & 0x000000FF);
 
-               pr_debug("PROGRAM: (%x) %x %x %x\n",
+               dev_dbg(&spi->dev, "PROGRAM: (%x) %x %x %x\n",
                        command[0], command[1], command[2], command[3]);
 
                x[1].tx_buf = writebuf;
@@ -383,8 +384,8 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
                status = spi_sync(spi, &msg);
                spi_transfer_del(x + 1);
                if (status < 0)
-                       pr_debug("%s: pgm %u/%u -> %d\n",
-                               dev_name(&spi->dev), addr, writelen, status);
+                       dev_dbg(&spi->dev, "pgm %u/%u -> %d\n",
+                               addr, writelen, status);
 
                (void) dataflash_waitready(priv->spi);
 
@@ -398,20 +399,20 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
                command[2] = (addr & 0x0000FF00) >> 8;
                command[3] = 0;
 
-               pr_debug("COMPARE: (%x) %x %x %x\n",
+               dev_dbg(&spi->dev, "COMPARE: (%x) %x %x %x\n",
                        command[0], command[1], command[2], command[3]);
 
                status = spi_sync(spi, &msg);
                if (status < 0)
-                       pr_debug("%s: compare %u -> %d\n",
-                               dev_name(&spi->dev), addr, status);
+                       dev_dbg(&spi->dev, "compare %u -> %d\n",
+                               addr, status);
 
                status = dataflash_waitready(priv->spi);
 
                /* Check result of the compare operation */
                if (status & (1 << 6)) {
-                       printk(KERN_ERR "%s: compare page %u, err %d\n",
-                               dev_name(&spi->dev), pageaddr, status);
+                       dev_err(&spi->dev, "compare page %u, err %d\n",
+                               pageaddr, status);
                        remaining = 0;
                        status = -EIO;
                        break;
@@ -455,11 +456,11 @@ static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len,
 }
 
 static ssize_t otp_read(struct spi_device *spi, unsigned base,
-               uint8_t *buf, loff_t off, size_t len)
+               u8 *buf, loff_t off, size_t len)
 {
        struct spi_message      m;
        size_t                  l;
-       uint8_t                 *scratch;
+       u8                      *scratch;
        struct spi_transfer     t;
        int                     status;
 
@@ -538,7 +539,7 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
 {
        struct spi_message      m;
        const size_t            l = 4 + 64;
-       uint8_t                 *scratch;
+       u8                      *scratch;
        struct spi_transfer     t;
        struct dataflash        *priv = mtd->priv;
        int                     status;
@@ -689,14 +690,15 @@ struct flash_info {
        /* JEDEC id has a high byte of zero plus three data bytes:
         * the manufacturer id, then a two byte device id.
         */
-       uint32_t        jedec_id;
+       u64             jedec_id;
 
        /* The size listed here is what works with OP_ERASE_PAGE. */
        unsigned        nr_pages;
-       uint16_t        pagesize;
-       uint16_t        pageoffset;
+       u16             pagesize;
+       u16             pageoffset;
 
-       uint16_t        flags;
+       u16             flags;
+#define SUP_EXTID      0x0004          /* supports extended ID data */
 #define SUP_POW2PS     0x0002          /* supports 2^N byte pages */
 #define IS_POW2PS      0x0001          /* uses 2^N byte pages */
 };
@@ -734,54 +736,32 @@ static struct flash_info dataflash_data[] = {
 
        { "AT45DB642x",  0x1f2800, 8192, 1056, 11, SUP_POW2PS},
        { "at45db642d",  0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS},
+
+       { "AT45DB641E",  0x1f28000100, 32768, 264, 9, SUP_EXTID | SUP_POW2PS},
+       { "at45db641e",  0x1f28000100, 32768, 256, 8, SUP_EXTID | SUP_POW2PS | IS_POW2PS},
 };
 
-static struct flash_info *jedec_probe(struct spi_device *spi)
+static struct flash_info *jedec_lookup(struct spi_device *spi,
+                                      u64 jedec, bool use_extid)
 {
-       int                     tmp;
-       uint8_t                 code = OP_READ_ID;
-       uint8_t                 id[3];
-       uint32_t                jedec;
-       struct flash_info       *info;
+       struct flash_info *info;
        int status;
 
-       /* JEDEC also defines an optional "extended device information"
-        * string for after vendor-specific data, after the three bytes
-        * we use here.  Supporting some chips might require using it.
-        *
-        * If the vendor ID isn't Atmel's (0x1f), assume this call failed.
-        * That's not an error; only rev C and newer chips handle it, and
-        * only Atmel sells these chips.
-        */
-       tmp = spi_write_then_read(spi, &code, 1, id, 3);
-       if (tmp < 0) {
-               pr_debug("%s: error %d reading JEDEC ID\n",
-                       dev_name(&spi->dev), tmp);
-               return ERR_PTR(tmp);
-       }
-       if (id[0] != 0x1f)
-               return NULL;
-
-       jedec = id[0];
-       jedec = jedec << 8;
-       jedec |= id[1];
-       jedec = jedec << 8;
-       jedec |= id[2];
+       for (info = dataflash_data;
+            info < dataflash_data + ARRAY_SIZE(dataflash_data);
+            info++) {
+               if (use_extid && !(info->flags & SUP_EXTID))
+                       continue;
 
-       for (tmp = 0, info = dataflash_data;
-                       tmp < ARRAY_SIZE(dataflash_data);
-                       tmp++, info++) {
                if (info->jedec_id == jedec) {
-                       pr_debug("%s: OTP, sector protect%s\n",
-                               dev_name(&spi->dev),
-                               (info->flags & SUP_POW2PS)
-                                       ? ", binary pagesize" : ""
-                               );
+                       dev_dbg(&spi->dev, "OTP, sector protect%s\n",
+                               (info->flags & SUP_POW2PS) ?
+                               ", binary pagesize" : "");
                        if (info->flags & SUP_POW2PS) {
                                status = dataflash_status(spi);
                                if (status < 0) {
-                                       pr_debug("%s: status error %d\n",
-                                               dev_name(&spi->dev), status);
+                                       dev_dbg(&spi->dev, "status error %d\n",
+                                               status);
                                        return ERR_PTR(status);
                                }
                                if (status & 0x1) {
@@ -796,12 +776,58 @@ static struct flash_info *jedec_probe(struct spi_device *spi)
                }
        }
 
+       return ERR_PTR(-ENODEV);
+}
+
+static struct flash_info *jedec_probe(struct spi_device *spi)
+{
+       int ret;
+       u8 code = OP_READ_ID;
+       u64 jedec;
+       u8 id[sizeof(jedec)] = {0};
+       const unsigned int id_size = 5;
+       struct flash_info *info;
+
+       /*
+        * JEDEC also defines an optional "extended device information"
+        * string for after vendor-specific data, after the three bytes
+        * we use here.  Supporting some chips might require using it.
+        *
+        * If the vendor ID isn't Atmel's (0x1f), assume this call failed.
+        * That's not an error; only rev C and newer chips handle it, and
+        * only Atmel sells these chips.
+        */
+       ret = spi_write_then_read(spi, &code, 1, id, id_size);
+       if (ret < 0) {
+               dev_dbg(&spi->dev, "error %d reading JEDEC ID\n", ret);
+               return ERR_PTR(ret);
+       }
+
+       if (id[0] != CFI_MFR_ATMEL)
+               return NULL;
+
+       jedec = be64_to_cpup((__be64 *)id);
+
+       /*
+        * First, try to match device using extended device
+        * information
+        */
+       info = jedec_lookup(spi, jedec >> DATAFLASH_SHIFT_EXTID, true);
+       if (!IS_ERR(info))
+               return info;
+       /*
+        * If that fails, make another pass using regular ID
+        * information
+        */
+       info = jedec_lookup(spi, jedec >> DATAFLASH_SHIFT_ID, false);
+       if (!IS_ERR(info))
+               return info;
        /*
         * Treat other chips as errors ... we won't know the right page
         * size (it might be binary) even when we can tell which density
         * class is involved (legacy chip id scheme).
         */
-       dev_warn(&spi->dev, "JEDEC id %06x not handled\n", jedec);
+       dev_warn(&spi->dev, "JEDEC id %016llx not handled\n", jedec);
        return ERR_PTR(-ENODEV);
 }
 
@@ -845,8 +871,7 @@ static int dataflash_probe(struct spi_device *spi)
         */
        status = dataflash_status(spi);
        if (status <= 0 || status == 0xff) {
-               pr_debug("%s: status error %d\n",
-                               dev_name(&spi->dev), status);
+               dev_dbg(&spi->dev, "status error %d\n", status);
                if (status == 0 || status == 0xff)
                        status = -ENODEV;
                return status;
@@ -887,8 +912,7 @@ static int dataflash_probe(struct spi_device *spi)
        }
 
        if (status < 0)
-               pr_debug("%s: add_dataflash --> %d\n", dev_name(&spi->dev),
-                               status);
+               dev_dbg(&spi->dev, "add_dataflash --> %d\n", status);
 
        return status;
 }
@@ -898,7 +922,7 @@ static int dataflash_remove(struct spi_device *spi)
        struct dataflash        *flash = spi_get_drvdata(spi);
        int                     status;
 
-       pr_debug("%s: remove\n", dev_name(&spi->dev));
+       dev_dbg(&spi->dev, "remove\n");
 
        status = mtd_device_unregister(&flash->mtd);
        if (status == 0)
index 8b81e15105dd4a6cc34b90ed8f50b08aa41ac9fa..eba125c9f23f485cf452739c5c5056cd440c1ccd 100644 (file)
@@ -13,7 +13,6 @@
 #define _MTD_SERIAL_FLASH_CMDS_H
 
 /* Generic Flash Commands/OPCODEs */
-#define SPINOR_OP_RDSR2                0x35
 #define SPINOR_OP_WRVCR                0x81
 #define SPINOR_OP_RDVCR                0x85
 
index 804313a33f2bec433010350f47ef063313354f94..21afd94cd904a0b0fe71e71a39345f938a0e9bc0 100644 (file)
@@ -1445,7 +1445,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
        }
 
        /* Check status of 'QE' bit, update if required. */
-       stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1);
+       stfsm_read_status(fsm, SPINOR_OP_RDCR, &cr1, 1);
        data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
        if (data_pads == 4) {
                if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
@@ -1490,7 +1490,7 @@ static int stfsm_w25q_config(struct stfsm *fsm)
                return ret;
 
        /* Check status of 'QE' bit, update if required. */
-       stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1);
+       stfsm_read_status(fsm, SPINOR_OP_RDCR, &sr2, 1);
        data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
        if (data_pads == 4) {
                if (!(sr2 & W25Q_STATUS_QE)) {
index 9d371cd728ea122e47000139f35b451dd84df032..05b286b5289f547020622a6883318cad15ec010f 100644 (file)
@@ -59,7 +59,7 @@ int of_flash_probe_gemini(struct platform_device *pdev,
                          struct device_node *np,
                          struct map_info *map)
 {
-       static struct regmap *rmap;
+       struct regmap *rmap;
        struct device *dev = &pdev->dev;
        u32 val;
        int ret;
index 1517da3ddd7d0b9752f8b7d4ebea8aaa3a6ae298..956382cea2568b3ee9c8721e5ab2acd522ce66f5 100644 (file)
@@ -991,7 +991,7 @@ EXPORT_SYMBOL_GPL(mtd_point);
 /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
 int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 {
-       if (!mtd->_point)
+       if (!mtd->_unpoint)
                return -EOPNOTSUPP;
        if (from < 0 || from >= mtd->size || len > mtd->size - from)
                return -EINVAL;
index ea5e5307f667f2a6669287377a633824bb67a4bb..5736b0c90b339b6bc3e8e1ae3189341910b958f2 100644 (file)
 static LIST_HEAD(mtd_partitions);
 static DEFINE_MUTEX(mtd_partitions_mutex);
 
-/* Our partition node structure */
+/**
+ * struct mtd_part - our partition node structure
+ *
+ * @mtd: struct holding partition details
+ * @parent: parent mtd - flash device or another partition
+ * @offset: partition offset relative to the *flash device*
+ */
 struct mtd_part {
        struct mtd_info mtd;
-       struct mtd_info *master;
+       struct mtd_info *parent;
        uint64_t offset;
        struct list_head list;
 };
@@ -67,15 +73,15 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
        struct mtd_ecc_stats stats;
        int res;
 
-       stats = part->master->ecc_stats;
-       res = part->master->_read(part->master, from + part->offset, len,
+       stats = part->parent->ecc_stats;
+       res = part->parent->_read(part->parent, from + part->offset, len,
                                  retlen, buf);
        if (unlikely(mtd_is_eccerr(res)))
                mtd->ecc_stats.failed +=
-                       part->master->ecc_stats.failed - stats.failed;
+                       part->parent->ecc_stats.failed - stats.failed;
        else
                mtd->ecc_stats.corrected +=
-                       part->master->ecc_stats.corrected - stats.corrected;
+                       part->parent->ecc_stats.corrected - stats.corrected;
        return res;
 }
 
@@ -84,7 +90,7 @@ static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
 {
        struct mtd_part *part = mtd_to_part(mtd);
 
-       return part->master->_point(part->master, from + part->offset, len,
+       return part->parent->_point(part->parent, from + part->offset, len,
                                    retlen, virt, phys);
 }
 
@@ -92,7 +98,7 @@ static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 {
        struct mtd_part *part = mtd_to_part(mtd);
 
-       return part->master->_unpoint(part->master, from + part->offset, len);
+       return part->parent->_unpoint(part->parent, from + part->offset, len);
 }
 
 static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
@@ -103,7 +109,7 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
        struct mtd_part *part = mtd_to_part(mtd);
 
        offset += part->offset;
-       return part->master->_get_unmapped_area(part->master, len, offset,
+       return part->parent->_get_unmapped_area(part->parent, len, offset,
                                                flags);
 }
 
@@ -132,7 +138,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
                        return -EINVAL;
        }
 
-       res = part->master->_read_oob(part->master, from + part->offset, ops);
+       res = part->parent->_read_oob(part->parent, from + part->offset, ops);
        if (unlikely(res)) {
                if (mtd_is_bitflip(res))
                        mtd->ecc_stats.corrected++;
@@ -146,7 +152,7 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
                size_t len, size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_read_user_prot_reg(part->master, from, len,
+       return part->parent->_read_user_prot_reg(part->parent, from, len,
                                                 retlen, buf);
 }
 
@@ -154,7 +160,7 @@ static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
                                   size_t *retlen, struct otp_info *buf)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_get_user_prot_info(part->master, len, retlen,
+       return part->parent->_get_user_prot_info(part->parent, len, retlen,
                                                 buf);
 }
 
@@ -162,7 +168,7 @@ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
                size_t len, size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_read_fact_prot_reg(part->master, from, len,
+       return part->parent->_read_fact_prot_reg(part->parent, from, len,
                                                 retlen, buf);
 }
 
@@ -170,7 +176,7 @@ static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
                                   size_t *retlen, struct otp_info *buf)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_get_fact_prot_info(part->master, len, retlen,
+       return part->parent->_get_fact_prot_info(part->parent, len, retlen,
                                                 buf);
 }
 
@@ -178,7 +184,7 @@ static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_write(part->master, to + part->offset, len,
+       return part->parent->_write(part->parent, to + part->offset, len,
                                    retlen, buf);
 }
 
@@ -186,7 +192,7 @@ static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
                size_t *retlen, const u_char *buf)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_panic_write(part->master, to + part->offset, len,
+       return part->parent->_panic_write(part->parent, to + part->offset, len,
                                          retlen, buf);
 }
 
@@ -199,14 +205,14 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
                return -EINVAL;
        if (ops->datbuf && to + ops->len > mtd->size)
                return -EINVAL;
-       return part->master->_write_oob(part->master, to + part->offset, ops);
+       return part->parent->_write_oob(part->parent, to + part->offset, ops);
 }
 
 static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
                size_t len, size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_write_user_prot_reg(part->master, from, len,
+       return part->parent->_write_user_prot_reg(part->parent, from, len,
                                                  retlen, buf);
 }
 
@@ -214,14 +220,14 @@ static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
                size_t len)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_lock_user_prot_reg(part->master, from, len);
+       return part->parent->_lock_user_prot_reg(part->parent, from, len);
 }
 
 static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
                unsigned long count, loff_t to, size_t *retlen)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_writev(part->master, vecs, count,
+       return part->parent->_writev(part->parent, vecs, count,
                                     to + part->offset, retlen);
 }
 
@@ -231,7 +237,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
        int ret;
 
        instr->addr += part->offset;
-       ret = part->master->_erase(part->master, instr);
+       ret = part->parent->_erase(part->parent, instr);
        if (ret) {
                if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
                        instr->fail_addr -= part->offset;
@@ -257,51 +263,51 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback);
 static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_lock(part->master, ofs + part->offset, len);
+       return part->parent->_lock(part->parent, ofs + part->offset, len);
 }
 
 static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_unlock(part->master, ofs + part->offset, len);
+       return part->parent->_unlock(part->parent, ofs + part->offset, len);
 }
 
 static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_is_locked(part->master, ofs + part->offset, len);
+       return part->parent->_is_locked(part->parent, ofs + part->offset, len);
 }
 
 static void part_sync(struct mtd_info *mtd)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       part->master->_sync(part->master);
+       part->parent->_sync(part->parent);
 }
 
 static int part_suspend(struct mtd_info *mtd)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_suspend(part->master);
+       return part->parent->_suspend(part->parent);
 }
 
 static void part_resume(struct mtd_info *mtd)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       part->master->_resume(part->master);
+       part->parent->_resume(part->parent);
 }
 
 static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs)
 {
        struct mtd_part *part = mtd_to_part(mtd);
        ofs += part->offset;
-       return part->master->_block_isreserved(part->master, ofs);
+       return part->parent->_block_isreserved(part->parent, ofs);
 }
 
 static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
 {
        struct mtd_part *part = mtd_to_part(mtd);
        ofs += part->offset;
-       return part->master->_block_isbad(part->master, ofs);
+       return part->parent->_block_isbad(part->parent, ofs);
 }
 
 static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
@@ -310,7 +316,7 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
        int res;
 
        ofs += part->offset;
-       res = part->master->_block_markbad(part->master, ofs);
+       res = part->parent->_block_markbad(part->parent, ofs);
        if (!res)
                mtd->ecc_stats.badblocks++;
        return res;
@@ -319,13 +325,13 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
 static int part_get_device(struct mtd_info *mtd)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       return part->master->_get_device(part->master);
+       return part->parent->_get_device(part->parent);
 }
 
 static void part_put_device(struct mtd_info *mtd)
 {
        struct mtd_part *part = mtd_to_part(mtd);
-       part->master->_put_device(part->master);
+       part->parent->_put_device(part->parent);
 }
 
 static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
@@ -333,7 +339,7 @@ static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
 {
        struct mtd_part *part = mtd_to_part(mtd);
 
-       return mtd_ooblayout_ecc(part->master, section, oobregion);
+       return mtd_ooblayout_ecc(part->parent, section, oobregion);
 }
 
 static int part_ooblayout_free(struct mtd_info *mtd, int section,
@@ -341,7 +347,7 @@ static int part_ooblayout_free(struct mtd_info *mtd, int section,
 {
        struct mtd_part *part = mtd_to_part(mtd);
 
-       return mtd_ooblayout_free(part->master, section, oobregion);
+       return mtd_ooblayout_free(part->parent, section, oobregion);
 }
 
 static const struct mtd_ooblayout_ops part_ooblayout_ops = {
@@ -353,7 +359,7 @@ static int part_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
 {
        struct mtd_part *part = mtd_to_part(mtd);
 
-       return part->master->_max_bad_blocks(part->master,
+       return part->parent->_max_bad_blocks(part->parent,
                                             ofs + part->offset, len);
 }
 
@@ -363,63 +369,70 @@ static inline void free_partition(struct mtd_part *p)
        kfree(p);
 }
 
-/*
- * This function unregisters and destroy all slave MTD objects which are
- * attached to the given master MTD object.
+/**
+ * mtd_parse_part - parse MTD partition looking for subpartitions
+ *
+ * @slave: part that is supposed to be a container and should be parsed
+ * @types: NULL-terminated array with names of partition parsers to try
+ *
+ * Some partitions are kind of containers with extra subpartitions (volumes).
+ * There can be various formats of such containers. This function tries to use
+ * specified parsers to analyze given partition and registers found
+ * subpartitions on success.
  */
-
-int del_mtd_partitions(struct mtd_info *master)
+static int mtd_parse_part(struct mtd_part *slave, const char *const *types)
 {
-       struct mtd_part *slave, *next;
-       int ret, err = 0;
+       struct mtd_partitions parsed;
+       int err;
 
-       mutex_lock(&mtd_partitions_mutex);
-       list_for_each_entry_safe(slave, next, &mtd_partitions, list)
-               if (slave->master == master) {
-                       ret = del_mtd_device(&slave->mtd);
-                       if (ret < 0) {
-                               err = ret;
-                               continue;
-                       }
-                       list_del(&slave->list);
-                       free_partition(slave);
-               }
-       mutex_unlock(&mtd_partitions_mutex);
+       err = parse_mtd_partitions(&slave->mtd, types, &parsed, NULL);
+       if (err)
+               return err;
+       else if (!parsed.nr_parts)
+               return -ENOENT;
+
+       err = add_mtd_partitions(&slave->mtd, parsed.parts, parsed.nr_parts);
+
+       mtd_part_parser_cleanup(&parsed);
 
        return err;
 }
 
-static struct mtd_part *allocate_partition(struct mtd_info *master,
+static struct mtd_part *allocate_partition(struct mtd_info *parent,
                        const struct mtd_partition *part, int partno,
                        uint64_t cur_offset)
 {
+       int wr_alignment = (parent->flags & MTD_NO_ERASE) ? parent->writesize :
+                                                           parent->erasesize;
        struct mtd_part *slave;
+       u32 remainder;
        char *name;
+       u64 tmp;
 
        /* allocate the partition structure */
        slave = kzalloc(sizeof(*slave), GFP_KERNEL);
        name = kstrdup(part->name, GFP_KERNEL);
        if (!name || !slave) {
                printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
-                      master->name);
+                      parent->name);
                kfree(name);
                kfree(slave);
                return ERR_PTR(-ENOMEM);
        }
 
        /* set up the MTD object for this partition */
-       slave->mtd.type = master->type;
-       slave->mtd.flags = master->flags & ~part->mask_flags;
+       slave->mtd.type = parent->type;
+       slave->mtd.flags = parent->flags & ~part->mask_flags;
        slave->mtd.size = part->size;
-       slave->mtd.writesize = master->writesize;
-       slave->mtd.writebufsize = master->writebufsize;
-       slave->mtd.oobsize = master->oobsize;
-       slave->mtd.oobavail = master->oobavail;
-       slave->mtd.subpage_sft = master->subpage_sft;
-       slave->mtd.pairing = master->pairing;
+       slave->mtd.writesize = parent->writesize;
+       slave->mtd.writebufsize = parent->writebufsize;
+       slave->mtd.oobsize = parent->oobsize;
+       slave->mtd.oobavail = parent->oobavail;
+       slave->mtd.subpage_sft = parent->subpage_sft;
+       slave->mtd.pairing = parent->pairing;
 
        slave->mtd.name = name;
-       slave->mtd.owner = master->owner;
+       slave->mtd.owner = parent->owner;
 
        /* NOTE: Historically, we didn't arrange MTDs as a tree out of
         * concern for showing the same data in multiple partitions.
@@ -429,80 +442,81 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
         * parent conditional on that option. Note, this is a way to
         * distinguish between the master and the partition in sysfs.
         */
-       slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ?
-                               &master->dev :
-                               master->dev.parent;
+       slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ?
+                               &parent->dev :
+                               parent->dev.parent;
        slave->mtd.dev.of_node = part->of_node;
 
        slave->mtd._read = part_read;
        slave->mtd._write = part_write;
 
-       if (master->_panic_write)
+       if (parent->_panic_write)
                slave->mtd._panic_write = part_panic_write;
 
-       if (master->_point && master->_unpoint) {
+       if (parent->_point && parent->_unpoint) {
                slave->mtd._point = part_point;
                slave->mtd._unpoint = part_unpoint;
        }
 
-       if (master->_get_unmapped_area)
+       if (parent->_get_unmapped_area)
                slave->mtd._get_unmapped_area = part_get_unmapped_area;
-       if (master->_read_oob)
+       if (parent->_read_oob)
                slave->mtd._read_oob = part_read_oob;
-       if (master->_write_oob)
+       if (parent->_write_oob)
                slave->mtd._write_oob = part_write_oob;
-       if (master->_read_user_prot_reg)
+       if (parent->_read_user_prot_reg)
                slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
-       if (master->_read_fact_prot_reg)
+       if (parent->_read_fact_prot_reg)
                slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
-       if (master->_write_user_prot_reg)
+       if (parent->_write_user_prot_reg)
                slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
-       if (master->_lock_user_prot_reg)
+       if (parent->_lock_user_prot_reg)
                slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
-       if (master->_get_user_prot_info)
+       if (parent->_get_user_prot_info)
                slave->mtd._get_user_prot_info = part_get_user_prot_info;
-       if (master->_get_fact_prot_info)
+       if (parent->_get_fact_prot_info)
                slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
-       if (master->_sync)
+       if (parent->_sync)
                slave->mtd._sync = part_sync;
-       if (!partno && !master->dev.class && master->_suspend &&
-           master->_resume) {
-                       slave->mtd._suspend = part_suspend;
-                       slave->mtd._resume = part_resume;
+       if (!partno && !parent->dev.class && parent->_suspend &&
+           parent->_resume) {
+               slave->mtd._suspend = part_suspend;
+               slave->mtd._resume = part_resume;
        }
-       if (master->_writev)
+       if (parent->_writev)
                slave->mtd._writev = part_writev;
-       if (master->_lock)
+       if (parent->_lock)
                slave->mtd._lock = part_lock;
-       if (master->_unlock)
+       if (parent->_unlock)
                slave->mtd._unlock = part_unlock;
-       if (master->_is_locked)
+       if (parent->_is_locked)
                slave->mtd._is_locked = part_is_locked;
-       if (master->_block_isreserved)
+       if (parent->_block_isreserved)
                slave->mtd._block_isreserved = part_block_isreserved;
-       if (master->_block_isbad)
+       if (parent->_block_isbad)
                slave->mtd._block_isbad = part_block_isbad;
-       if (master->_block_markbad)
+       if (parent->_block_markbad)
                slave->mtd._block_markbad = part_block_markbad;
-       if (master->_max_bad_blocks)
+       if (parent->_max_bad_blocks)
                slave->mtd._max_bad_blocks = part_max_bad_blocks;
 
-       if (master->_get_device)
+       if (parent->_get_device)
                slave->mtd._get_device = part_get_device;
-       if (master->_put_device)
+       if (parent->_put_device)
                slave->mtd._put_device = part_put_device;
 
        slave->mtd._erase = part_erase;
-       slave->master = master;
+       slave->parent = parent;
        slave->offset = part->offset;
 
        if (slave->offset == MTDPART_OFS_APPEND)
                slave->offset = cur_offset;
        if (slave->offset == MTDPART_OFS_NXTBLK) {
+               tmp = cur_offset;
                slave->offset = cur_offset;
-               if (mtd_mod_by_eb(cur_offset, master) != 0) {
-                       /* Round up to next erasesize */
-                       slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
+               remainder = do_div(tmp, wr_alignment);
+               if (remainder) {
+                       slave->offset += wr_alignment - remainder;
                        printk(KERN_NOTICE "Moving partition %d: "
                               "0x%012llx -> 0x%012llx\n", partno,
                               (unsigned long long)cur_offset, (unsigned long long)slave->offset);
@@ -510,25 +524,25 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
        }
        if (slave->offset == MTDPART_OFS_RETAIN) {
                slave->offset = cur_offset;
-               if (master->size - slave->offset >= slave->mtd.size) {
-                       slave->mtd.size = master->size - slave->offset
+               if (parent->size - slave->offset >= slave->mtd.size) {
+                       slave->mtd.size = parent->size - slave->offset
                                                        - slave->mtd.size;
                } else {
                        printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
-                               part->name, master->size - slave->offset,
+                               part->name, parent->size - slave->offset,
                                slave->mtd.size);
                        /* register to preserve ordering */
                        goto out_register;
                }
        }
        if (slave->mtd.size == MTDPART_SIZ_FULL)
-               slave->mtd.size = master->size - slave->offset;
+               slave->mtd.size = parent->size - slave->offset;
 
        printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
                (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
 
        /* let's do some sanity checks */
-       if (slave->offset >= master->size) {
+       if (slave->offset >= parent->size) {
                /* let's register it anyway to preserve ordering */
                slave->offset = 0;
                slave->mtd.size = 0;
@@ -536,16 +550,16 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
                        part->name);
                goto out_register;
        }
-       if (slave->offset + slave->mtd.size > master->size) {
-               slave->mtd.size = master->size - slave->offset;
+       if (slave->offset + slave->mtd.size > parent->size) {
+               slave->mtd.size = parent->size - slave->offset;
                printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
-                       part->name, master->name, (unsigned long long)slave->mtd.size);
+                       part->name, parent->name, (unsigned long long)slave->mtd.size);
        }
-       if (master->numeraseregions > 1) {
+       if (parent->numeraseregions > 1) {
                /* Deal with variable erase size stuff */
-               int i, max = master->numeraseregions;
+               int i, max = parent->numeraseregions;
                u64 end = slave->offset + slave->mtd.size;
-               struct mtd_erase_region_info *regions = master->eraseregions;
+               struct mtd_erase_region_info *regions = parent->eraseregions;
 
                /* Find the first erase regions which is part of this
                 * partition. */
@@ -564,37 +578,40 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
                BUG_ON(slave->mtd.erasesize == 0);
        } else {
                /* Single erase size */
-               slave->mtd.erasesize = master->erasesize;
+               slave->mtd.erasesize = parent->erasesize;
        }
 
-       if ((slave->mtd.flags & MTD_WRITEABLE) &&
-           mtd_mod_by_eb(slave->offset, &slave->mtd)) {
+       tmp = slave->offset;
+       remainder = do_div(tmp, wr_alignment);
+       if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
                /* Doesn't start on a boundary of major erase size */
                /* FIXME: Let it be writable if it is on a boundary of
                 * _minor_ erase size though */
                slave->mtd.flags &= ~MTD_WRITEABLE;
-               printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
+               printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
                        part->name);
        }
-       if ((slave->mtd.flags & MTD_WRITEABLE) &&
-           mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
+
+       tmp = slave->mtd.size;
+       remainder = do_div(tmp, wr_alignment);
+       if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
                slave->mtd.flags &= ~MTD_WRITEABLE;
-               printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
+               printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
                        part->name);
        }
 
        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;
+       slave->mtd.ecc_step_size = parent->ecc_step_size;
+       slave->mtd.ecc_strength = parent->ecc_strength;
+       slave->mtd.bitflip_threshold = parent->bitflip_threshold;
 
-       if (master->_block_isbad) {
+       if (parent->_block_isbad) {
                uint64_t offs = 0;
 
                while (offs < slave->mtd.size) {
-                       if (mtd_block_isreserved(master, offs + slave->offset))
+                       if (mtd_block_isreserved(parent, offs + slave->offset))
                                slave->mtd.ecc_stats.bbtblocks++;
-                       else if (mtd_block_isbad(master, offs + slave->offset))
+                       else if (mtd_block_isbad(parent, offs + slave->offset))
                                slave->mtd.ecc_stats.badblocks++;
                        offs += slave->mtd.erasesize;
                }
@@ -628,7 +645,7 @@ static int mtd_add_partition_attrs(struct mtd_part *new)
        return ret;
 }
 
-int mtd_add_partition(struct mtd_info *master, const char *name,
+int mtd_add_partition(struct mtd_info *parent, const char *name,
                      long long offset, long long length)
 {
        struct mtd_partition part;
@@ -641,7 +658,7 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
                return -EINVAL;
 
        if (length == MTDPART_SIZ_FULL)
-               length = master->size - offset;
+               length = parent->size - offset;
 
        if (length <= 0)
                return -EINVAL;
@@ -651,7 +668,7 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
        part.size = length;
        part.offset = offset;
 
-       new = allocate_partition(master, &part, -1, offset);
+       new = allocate_partition(parent, &part, -1, offset);
        if (IS_ERR(new))
                return PTR_ERR(new);
 
@@ -667,23 +684,69 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
 }
 EXPORT_SYMBOL_GPL(mtd_add_partition);
 
-int mtd_del_partition(struct mtd_info *master, int partno)
+/**
+ * __mtd_del_partition - delete MTD partition
+ *
+ * @priv: internal MTD struct for partition to be deleted
+ *
+ * This function must be called with the partitions mutex locked.
+ */
+static int __mtd_del_partition(struct mtd_part *priv)
+{
+       struct mtd_part *child, *next;
+       int err;
+
+       list_for_each_entry_safe(child, next, &mtd_partitions, list) {
+               if (child->parent == &priv->mtd) {
+                       err = __mtd_del_partition(child);
+                       if (err)
+                               return err;
+               }
+       }
+
+       sysfs_remove_files(&priv->mtd.dev.kobj, mtd_partition_attrs);
+
+       err = del_mtd_device(&priv->mtd);
+       if (err)
+               return err;
+
+       list_del(&priv->list);
+       free_partition(priv);
+
+       return 0;
+}
+
+/*
+ * This function unregisters and destroy all slave MTD objects which are
+ * attached to the given MTD object.
+ */
+int del_mtd_partitions(struct mtd_info *mtd)
 {
        struct mtd_part *slave, *next;
-       int ret = -EINVAL;
+       int ret, err = 0;
 
        mutex_lock(&mtd_partitions_mutex);
        list_for_each_entry_safe(slave, next, &mtd_partitions, list)
-               if ((slave->master == master) &&
-                   (slave->mtd.index == partno)) {
-                       sysfs_remove_files(&slave->mtd.dev.kobj,
-                                          mtd_partition_attrs);
-                       ret = del_mtd_device(&slave->mtd);
+               if (slave->parent == mtd) {
+                       ret = __mtd_del_partition(slave);
                        if (ret < 0)
-                               break;
+                               err = ret;
+               }
+       mutex_unlock(&mtd_partitions_mutex);
+
+       return err;
+}
+
+int mtd_del_partition(struct mtd_info *mtd, int partno)
+{
+       struct mtd_part *slave, *next;
+       int ret = -EINVAL;
 
-                       list_del(&slave->list);
-                       free_partition(slave);
+       mutex_lock(&mtd_partitions_mutex);
+       list_for_each_entry_safe(slave, next, &mtd_partitions, list)
+               if ((slave->parent == mtd) &&
+                   (slave->mtd.index == partno)) {
+                       ret = __mtd_del_partition(slave);
                        break;
                }
        mutex_unlock(&mtd_partitions_mutex);
@@ -724,6 +787,8 @@ int add_mtd_partitions(struct mtd_info *master,
 
                add_mtd_device(&slave->mtd);
                mtd_add_partition_attrs(slave);
+               if (parts[i].types)
+                       mtd_parse_part(slave, parts[i].types);
 
                cur_offset = slave->offset + slave->mtd.size;
        }
@@ -799,6 +864,27 @@ static const char * const default_mtd_part_types[] = {
        NULL
 };
 
+static int mtd_part_do_parse(struct mtd_part_parser *parser,
+                            struct mtd_info *master,
+                            struct mtd_partitions *pparts,
+                            struct mtd_part_parser_data *data)
+{
+       int ret;
+
+       ret = (*parser->parse_fn)(master, &pparts->parts, data);
+       pr_debug("%s: parser %s: %i\n", master->name, parser->name, ret);
+       if (ret <= 0)
+               return ret;
+
+       pr_notice("%d %s partitions found on MTD device %s\n", ret,
+                 parser->name, master->name);
+
+       pparts->nr_parts = ret;
+       pparts->parser = parser;
+
+       return ret;
+}
+
 /**
  * parse_mtd_partitions - parse MTD partitions
  * @master: the master partition (describes whole MTD device)
@@ -839,16 +925,10 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
                         parser ? parser->name : NULL);
                if (!parser)
                        continue;
-               ret = (*parser->parse_fn)(master, &pparts->parts, data);
-               pr_debug("%s: parser %s: %i\n",
-                        master->name, parser->name, ret);
-               if (ret > 0) {
-                       printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
-                              ret, parser->name, master->name);
-                       pparts->nr_parts = ret;
-                       pparts->parser = parser;
+               ret = mtd_part_do_parse(parser, master, pparts, data);
+               /* Found partitions! */
+               if (ret > 0)
                        return 0;
-               }
                mtd_part_parser_put(parser);
                /*
                 * Stash the first error we see; only report it if no parser
@@ -899,6 +979,6 @@ uint64_t mtd_get_device_size(const struct mtd_info *mtd)
        if (!mtd_is_partition(mtd))
                return mtd->size;
 
-       return mtd_to_part(mtd)->master->size;
+       return mtd_get_device_size(mtd_to_part(mtd)->parent);
 }
 EXPORT_SYMBOL_GPL(mtd_get_device_size);
index c3029528063b8578e79746c51038d643631920b2..dbfa72d61d5aa7b955e6d0728b127f69e79b3012 100644 (file)
@@ -308,6 +308,7 @@ config MTD_NAND_CS553X
 config MTD_NAND_ATMEL
        tristate "Support for NAND Flash / SmartMedia on AT91"
        depends on ARCH_AT91
+       select MFD_ATMEL_SMC
        help
          Enables support for NAND Flash / Smart Media Card interface
          on Atmel AT91 processors.
@@ -542,6 +543,7 @@ config MTD_NAND_SUNXI
 
 config MTD_NAND_HISI504
        tristate "Support for NAND controller on Hisilicon SoC Hip04"
+       depends on ARCH_HISI || COMPILE_TEST
        depends on HAS_DMA
        help
          Enables support for NAND controller on Hisilicon SoC Hip04.
@@ -555,6 +557,7 @@ config MTD_NAND_QCOM
 
 config MTD_NAND_MTK
        tristate "Support for NAND controller on MTK SoCs"
+       depends on ARCH_MEDIATEK || COMPILE_TEST
        depends on HAS_DMA
        help
          Enables support for NAND controller on MTK SoCs.
index 3b24468961473e78f75acc254642973a8c15ff64..d922a88e407f119bbf52aae494c632e08d113e7f 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/interrupt.h>
 #include <linux/mfd/syscon.h>
 #include <linux/mfd/syscon/atmel-matrix.h>
+#include <linux/mfd/syscon/atmel-smc.h>
 #include <linux/module.h>
 #include <linux/mtd/nand.h>
 #include <linux/of_address.h>
@@ -64,7 +65,6 @@
 #include <linux/of_platform.h>
 #include <linux/iopoll.h>
 #include <linux/platform_device.h>
-#include <linux/platform_data/atmel.h>
 #include <linux/regmap.h>
 
 #include "pmecc.h"
@@ -151,6 +151,8 @@ struct atmel_nand_cs {
                void __iomem *virt;
                dma_addr_t dma;
        } io;
+
+       struct atmel_smc_cs_conf smcconf;
 };
 
 struct atmel_nand {
@@ -196,6 +198,8 @@ struct atmel_nand_controller_ops {
        void (*nand_init)(struct atmel_nand_controller *nc,
                          struct atmel_nand *nand);
        int (*ecc_init)(struct atmel_nand *nand);
+       int (*setup_data_interface)(struct atmel_nand *nand, int csline,
+                                   const struct nand_data_interface *conf);
 };
 
 struct atmel_nand_controller_caps {
@@ -912,7 +916,7 @@ static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
        struct mtd_info *mtd = nand_to_mtd(chip);
        struct atmel_nand *nand = to_atmel_nand(chip);
        struct atmel_hsmc_nand_controller *nc;
-       int ret;
+       int ret, status;
 
        nc = to_hsmc_nand_controller(chip->controller);
 
@@ -954,6 +958,10 @@ static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
                dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n",
                        ret);
 
+       status = chip->waitfunc(mtd, chip);
+       if (status & NAND_STATUS_FAIL)
+               return -EIO;
+
        return ret;
 }
 
@@ -1175,6 +1183,295 @@ static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand)
        return 0;
 }
 
+static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
+                                       const struct nand_data_interface *conf,
+                                       struct atmel_smc_cs_conf *smcconf)
+{
+       u32 ncycles, totalcycles, timeps, mckperiodps;
+       struct atmel_nand_controller *nc;
+       int ret;
+
+       nc = to_nand_controller(nand->base.controller);
+
+       /* DDR interface not supported. */
+       if (conf->type != NAND_SDR_IFACE)
+               return -ENOTSUPP;
+
+       /*
+        * tRC < 30ns implies EDO mode. This controller does not support this
+        * mode.
+        */
+       if (conf->timings.sdr.tRC_min < 30)
+               return -ENOTSUPP;
+
+       atmel_smc_cs_conf_init(smcconf);
+
+       mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck);
+       mckperiodps *= 1000;
+
+       /*
+        * Set write pulse timing. This one is easy to extract:
+        *
+        * NWE_PULSE = tWP
+        */
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps);
+       totalcycles = ncycles;
+       ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT,
+                                         ncycles);
+       if (ret)
+               return ret;
+
+       /*
+        * The write setup timing depends on the operation done on the NAND.
+        * All operations goes through the same data bus, but the operation
+        * type depends on the address we are writing to (ALE/CLE address
+        * lines).
+        * Since we have no way to differentiate the different operations at
+        * the SMC level, we must consider the worst case (the biggest setup
+        * time among all operation types):
+        *
+        * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE
+        */
+       timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min,
+                     conf->timings.sdr.tALS_min);
+       timeps = max(timeps, conf->timings.sdr.tDS_min);
+       ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+       ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0;
+       totalcycles += ncycles;
+       ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT,
+                                         ncycles);
+       if (ret)
+               return ret;
+
+       /*
+        * As for the write setup timing, the write hold timing depends on the
+        * operation done on the NAND:
+        *
+        * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH)
+        */
+       timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min,
+                     conf->timings.sdr.tALH_min);
+       timeps = max3(timeps, conf->timings.sdr.tDH_min,
+                     conf->timings.sdr.tWH_min);
+       ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+       totalcycles += ncycles;
+
+       /*
+        * The write cycle timing is directly matching tWC, but is also
+        * dependent on the other timings on the setup and hold timings we
+        * calculated earlier, which gives:
+        *
+        * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD)
+        */
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps);
+       ncycles = max(totalcycles, ncycles);
+       ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT,
+                                         ncycles);
+       if (ret)
+               return ret;
+
+       /*
+        * We don't want the CS line to be toggled between each byte/word
+        * transfer to the NAND. The only way to guarantee that is to have the
+        * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
+        *
+        * NCS_WR_PULSE = NWE_CYCLE
+        */
+       ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT,
+                                         ncycles);
+       if (ret)
+               return ret;
+
+       /*
+        * As for the write setup timing, the read hold timing depends on the
+        * operation done on the NAND:
+        *
+        * NRD_HOLD = max(tREH, tRHOH)
+        */
+       timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min);
+       ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+       totalcycles = ncycles;
+
+       /*
+        * TDF = tRHZ - NRD_HOLD
+        */
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps);
+       ncycles -= totalcycles;
+
+       /*
+        * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and
+        * we might end up with a config that does not fit in the TDF field.
+        * Just take the max value in this case and hope that the NAND is more
+        * tolerant than advertised.
+        */
+       if (ncycles > ATMEL_SMC_MODE_TDF_MAX)
+               ncycles = ATMEL_SMC_MODE_TDF_MAX;
+       else if (ncycles < ATMEL_SMC_MODE_TDF_MIN)
+               ncycles = ATMEL_SMC_MODE_TDF_MIN;
+
+       smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) |
+                        ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
+
+       /*
+        * Read pulse timing directly matches tRP:
+        *
+        * NRD_PULSE = tRP
+        */
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps);
+       totalcycles += ncycles;
+       ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT,
+                                         ncycles);
+       if (ret)
+               return ret;
+
+       /*
+        * The write cycle timing is directly matching tWC, but is also
+        * dependent on the setup and hold timings we calculated earlier,
+        * which gives:
+        *
+        * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD)
+        *
+        * NRD_SETUP is always 0.
+        */
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps);
+       ncycles = max(totalcycles, ncycles);
+       ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT,
+                                         ncycles);
+       if (ret)
+               return ret;
+
+       /*
+        * We don't want the CS line to be toggled between each byte/word
+        * transfer from the NAND. The only way to guarantee that is to have
+        * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
+        *
+        * NCS_RD_PULSE = NRD_CYCLE
+        */
+       ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT,
+                                         ncycles);
+       if (ret)
+               return ret;
+
+       /* Txxx timings are directly matching tXXX ones. */
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps);
+       ret = atmel_smc_cs_conf_set_timing(smcconf,
+                                          ATMEL_HSMC_TIMINGS_TCLR_SHIFT,
+                                          ncycles);
+       if (ret)
+               return ret;
+
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps);
+       ret = atmel_smc_cs_conf_set_timing(smcconf,
+                                          ATMEL_HSMC_TIMINGS_TADL_SHIFT,
+                                          ncycles);
+       if (ret)
+               return ret;
+
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps);
+       ret = atmel_smc_cs_conf_set_timing(smcconf,
+                                          ATMEL_HSMC_TIMINGS_TAR_SHIFT,
+                                          ncycles);
+       if (ret)
+               return ret;
+
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps);
+       ret = atmel_smc_cs_conf_set_timing(smcconf,
+                                          ATMEL_HSMC_TIMINGS_TRR_SHIFT,
+                                          ncycles);
+       if (ret)
+               return ret;
+
+       ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps);
+       ret = atmel_smc_cs_conf_set_timing(smcconf,
+                                          ATMEL_HSMC_TIMINGS_TWB_SHIFT,
+                                          ncycles);
+       if (ret)
+               return ret;
+
+       /* Attach the CS line to the NFC logic. */
+       smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL;
+
+       /* Set the appropriate data bus width. */
+       if (nand->base.options & NAND_BUSWIDTH_16)
+               smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
+
+       /* Operate in NRD/NWE READ/WRITEMODE. */
+       smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD |
+                        ATMEL_SMC_MODE_WRITEMODE_NWE;
+
+       return 0;
+}
+
+static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand,
+                                       int csline,
+                                       const struct nand_data_interface *conf)
+{
+       struct atmel_nand_controller *nc;
+       struct atmel_smc_cs_conf smcconf;
+       struct atmel_nand_cs *cs;
+       int ret;
+
+       nc = to_nand_controller(nand->base.controller);
+
+       ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
+       if (ret)
+               return ret;
+
+       if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+               return 0;
+
+       cs = &nand->cs[csline];
+       cs->smcconf = smcconf;
+       atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf);
+
+       return 0;
+}
+
+static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand,
+                                       int csline,
+                                       const struct nand_data_interface *conf)
+{
+       struct atmel_nand_controller *nc;
+       struct atmel_smc_cs_conf smcconf;
+       struct atmel_nand_cs *cs;
+       int ret;
+
+       nc = to_nand_controller(nand->base.controller);
+
+       ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
+       if (ret)
+               return ret;
+
+       if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+               return 0;
+
+       cs = &nand->cs[csline];
+       cs->smcconf = smcconf;
+
+       if (cs->rb.type == ATMEL_NAND_NATIVE_RB)
+               cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id);
+
+       atmel_hsmc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf);
+
+       return 0;
+}
+
+static int atmel_nand_setup_data_interface(struct mtd_info *mtd, int csline,
+                                       const struct nand_data_interface *conf)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_nand_controller *nc;
+
+       nc = to_nand_controller(nand->base.controller);
+
+       if (csline >= nand->numcs ||
+           (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
+               return -EINVAL;
+
+       return nc->caps->ops->setup_data_interface(nand, csline, conf);
+}
+
 static void atmel_nand_init(struct atmel_nand_controller *nc,
                            struct atmel_nand *nand)
 {
@@ -1192,6 +1489,9 @@ static void atmel_nand_init(struct atmel_nand_controller *nc,
        chip->write_buf = atmel_nand_write_buf;
        chip->select_chip = atmel_nand_select_chip;
 
+       if (nc->mck && nc->caps->ops->setup_data_interface)
+               chip->setup_data_interface = atmel_nand_setup_data_interface;
+
        /* Some NANDs require a longer delay than the default one (20us). */
        chip->chip_delay = 40;
 
@@ -1677,6 +1977,12 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
        if (nc->caps->legacy_of_bindings)
                return 0;
 
+       nc->mck = of_clk_get(dev->parent->of_node, 0);
+       if (IS_ERR(nc->mck)) {
+               dev_err(dev, "Failed to retrieve MCK clk\n");
+               return PTR_ERR(nc->mck);
+       }
+
        np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
        if (!np) {
                dev_err(dev, "Missing or invalid atmel,smc property\n");
@@ -1983,6 +2289,7 @@ static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
        .remove = atmel_hsmc_nand_controller_remove,
        .ecc_init = atmel_hsmc_nand_ecc_init,
        .nand_init = atmel_hsmc_nand_init,
+       .setup_data_interface = atmel_hsmc_nand_setup_data_interface,
 };
 
 static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
@@ -2037,7 +2344,14 @@ atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
        return 0;
 }
 
-static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
+/*
+ * The SMC reg layout of at91rm9200 is completely different which prevents us
+ * from re-using atmel_smc_nand_setup_data_interface() for the
+ * ->setup_data_interface() hook.
+ * At this point, there's no support for the at91rm9200 SMC IP, so we leave
+ * ->setup_data_interface() unassigned.
+ */
+static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
        .probe = atmel_smc_nand_controller_probe,
        .remove = atmel_smc_nand_controller_remove,
        .ecc_init = atmel_nand_ecc_init,
@@ -2045,6 +2359,20 @@ static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
 };
 
 static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
+       .ale_offs = BIT(21),
+       .cle_offs = BIT(22),
+       .ops = &at91rm9200_nc_ops,
+};
+
+static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
+       .probe = atmel_smc_nand_controller_probe,
+       .remove = atmel_smc_nand_controller_remove,
+       .ecc_init = atmel_nand_ecc_init,
+       .nand_init = atmel_smc_nand_init,
+       .setup_data_interface = atmel_smc_nand_setup_data_interface,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
        .ale_offs = BIT(21),
        .cle_offs = BIT(22),
        .ops = &atmel_smc_nc_ops,
@@ -2093,7 +2421,7 @@ static const struct of_device_id atmel_nand_controller_of_ids[] = {
        },
        {
                .compatible = "atmel,at91sam9260-nand-controller",
-               .data = &atmel_rm9200_nc_caps,
+               .data = &atmel_sam9260_nc_caps,
        },
        {
                .compatible = "atmel,at91sam9261-nand-controller",
@@ -2181,6 +2509,24 @@ static int atmel_nand_controller_remove(struct platform_device *pdev)
        return nc->caps->ops->remove(nc);
 }
 
+static __maybe_unused int atmel_nand_controller_resume(struct device *dev)
+{
+       struct atmel_nand_controller *nc = dev_get_drvdata(dev);
+       struct atmel_nand *nand;
+
+       list_for_each_entry(nand, &nc->chips, node) {
+               int i;
+
+               for (i = 0; i < nand->numcs; i++)
+                       nand_reset(&nand->base, i);
+       }
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL,
+                        atmel_nand_controller_resume);
+
 static struct platform_driver atmel_nand_controller_driver = {
        .driver = {
                .name = "atmel-nand-controller",
index f1da4ea88f2c01c4d28ae98a96df96b1a49eb3ef..54bac5b73f0ab39886ff2babe16f2a6c1bb49d4b 100644 (file)
@@ -392,6 +392,8 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
        b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte;
        b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf;
        b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf;
+       b47n->nand_chip.onfi_set_features = nand_onfi_get_set_features_notsupp;
+       b47n->nand_chip.onfi_get_features = nand_onfi_get_set_features_notsupp;
 
        nand_chip->chip_delay = 50;
        b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH;
index d40c32d311d8f9cb95ca20fd07cee000d7b69956..2fd733eba0a30fdc6bdbfb30ecbcc5de8a104775 100644 (file)
@@ -654,6 +654,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
        cafe->nand.read_buf = cafe_read_buf;
        cafe->nand.write_buf = cafe_write_buf;
        cafe->nand.select_chip = cafe_select_chip;
+       cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp;
+       cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp;
 
        cafe->nand.chip_delay = 0;
 
index 531c51991e5747b35fc68114ace9d534a1ea430e..7b26e53b95b1188c96dd348fa4efcd0dc575679f 100644 (file)
@@ -771,11 +771,14 @@ static int nand_davinci_probe(struct platform_device *pdev)
                        info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
                        info->chip.ecc.bytes = 10;
                        info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
+                       info->chip.ecc.algo = NAND_ECC_BCH;
                } else {
+                       /* 1bit ecc hamming */
                        info->chip.ecc.calculate = nand_davinci_calculate_1bit;
                        info->chip.ecc.correct = nand_davinci_correct_1bit;
                        info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
                        info->chip.ecc.bytes = 3;
+                       info->chip.ecc.algo = NAND_ECC_HAMMING;
                }
                info->chip.ecc.size = 512;
                info->chip.ecc.strength = pdata->ecc_bits;
index 16634df2e39a77ae1e34f68a7b4af0c642232b1e..d723be35214827edbada0ecf0d175c8ead3a62bd 100644 (file)
 #include <linux/mutex.h>
 #include <linux/mtd/mtd.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 
 #include "denali.h"
 
 MODULE_LICENSE("GPL");
 
-/*
- * We define a module parameter that allows the user to override
- * the hardware and decide what timing mode should be used.
- */
-#define NAND_DEFAULT_TIMINGS   -1
+#define DENALI_NAND_NAME    "denali-nand"
 
-static int onfi_timing_mode = NAND_DEFAULT_TIMINGS;
-module_param(onfi_timing_mode, int, S_IRUGO);
-MODULE_PARM_DESC(onfi_timing_mode,
-          "Overrides default ONFI setting. -1 indicates use default timings");
+/* Host Data/Command Interface */
+#define DENALI_HOST_ADDR       0x00
+#define DENALI_HOST_DATA       0x10
 
-#define DENALI_NAND_NAME    "denali-nand"
+#define DENALI_MAP00           (0 << 26)       /* direct access to buffer */
+#define DENALI_MAP01           (1 << 26)       /* read/write pages in PIO */
+#define DENALI_MAP10           (2 << 26)       /* high-level control plane */
+#define DENALI_MAP11           (3 << 26)       /* direct controller access */
 
-/*
- * We define a macro here that combines all interrupts this driver uses into
- * a single constant value, for convenience.
- */
-#define DENALI_IRQ_ALL (INTR__DMA_CMD_COMP | \
-                       INTR__ECC_TRANSACTION_DONE | \
-                       INTR__ECC_ERR | \
-                       INTR__PROGRAM_FAIL | \
-                       INTR__LOAD_COMP | \
-                       INTR__PROGRAM_COMP | \
-                       INTR__TIME_OUT | \
-                       INTR__ERASE_FAIL | \
-                       INTR__RST_COMP | \
-                       INTR__ERASE_COMP)
+/* MAP11 access cycle type */
+#define DENALI_MAP11_CMD       ((DENALI_MAP11) | 0)    /* command cycle */
+#define DENALI_MAP11_ADDR      ((DENALI_MAP11) | 1)    /* address cycle */
+#define DENALI_MAP11_DATA      ((DENALI_MAP11) | 2)    /* data cycle */
 
-/*
- * indicates whether or not the internal value for the flash bank is
- * valid or not
- */
-#define CHIP_SELECT_INVALID    -1
+/* MAP10 commands */
+#define DENALI_ERASE           0x01
+
+#define DENALI_BANK(denali)    ((denali)->active_bank << 24)
+
+#define DENALI_INVALID_BANK    -1
+#define DENALI_NR_BANKS                4
 
 /*
- * This macro divides two integers and rounds fractional values up
- * to the nearest integer value.
+ * The bus interface clock, clk_x, is phase aligned with the core clock.  The
+ * clk_x is an integral multiple N of the core clk.  The value N is configured
+ * at IP delivery time, and its available value is 4, 5, or 6.  We need to align
+ * to the largest value to make it work with any possible configuration.
  */
-#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y)))
+#define DENALI_CLK_X_MULT      6
 
 /*
  * this macro allows us to convert from an MTD structure to our own
@@ -77,339 +70,11 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
        return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
 }
 
-/*
- * These constants are defined by the driver to enable common driver
- * configuration options.
- */
-#define SPARE_ACCESS           0x41
-#define MAIN_ACCESS            0x42
-#define MAIN_SPARE_ACCESS      0x43
-
-#define DENALI_READ    0
-#define DENALI_WRITE   0x100
-
-/*
- * this is a helper macro that allows us to
- * format the bank into the proper bits for the controller
- */
-#define BANK(x) ((x) << 24)
-
-/* forward declarations */
-static void clear_interrupts(struct denali_nand_info *denali);
-static uint32_t wait_for_irq(struct denali_nand_info *denali,
-                                                       uint32_t irq_mask);
-static void denali_irq_enable(struct denali_nand_info *denali,
-                                                       uint32_t int_mask);
-static uint32_t read_interrupt_status(struct denali_nand_info *denali);
-
-/*
- * Certain operations for the denali NAND controller use an indexed mode to
- * read/write data. The operation is performed by writing the address value
- * of the command to the device memory followed by the data. This function
- * abstracts this common operation.
- */
-static void index_addr(struct denali_nand_info *denali,
-                               uint32_t address, uint32_t data)
-{
-       iowrite32(address, denali->flash_mem);
-       iowrite32(data, denali->flash_mem + 0x10);
-}
-
-/* Perform an indexed read of the device */
-static void index_addr_read_data(struct denali_nand_info *denali,
-                                uint32_t address, uint32_t *pdata)
-{
-       iowrite32(address, denali->flash_mem);
-       *pdata = ioread32(denali->flash_mem + 0x10);
-}
-
-/*
- * We need to buffer some data for some of the NAND core routines.
- * The operations manage buffering that data.
- */
-static void reset_buf(struct denali_nand_info *denali)
-{
-       denali->buf.head = denali->buf.tail = 0;
-}
-
-static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte)
-{
-       denali->buf.buf[denali->buf.tail++] = byte;
-}
-
-/* reads the status of the device */
-static void read_status(struct denali_nand_info *denali)
-{
-       uint32_t cmd;
-
-       /* initialize the data buffer to store status */
-       reset_buf(denali);
-
-       cmd = ioread32(denali->flash_reg + WRITE_PROTECT);
-       if (cmd)
-               write_byte_to_buf(denali, NAND_STATUS_WP);
-       else
-               write_byte_to_buf(denali, 0);
-}
-
-/* resets a specific device connected to the core */
-static void reset_bank(struct denali_nand_info *denali)
-{
-       uint32_t irq_status;
-       uint32_t irq_mask = INTR__RST_COMP | INTR__TIME_OUT;
-
-       clear_interrupts(denali);
-
-       iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
-
-       irq_status = wait_for_irq(denali, irq_mask);
-
-       if (irq_status & INTR__TIME_OUT)
-               dev_err(denali->dev, "reset bank failed.\n");
-}
-
-/* Reset the flash controller */
-static uint16_t denali_nand_reset(struct denali_nand_info *denali)
-{
-       int i;
-
-       for (i = 0; i < denali->max_banks; i++)
-               iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
-               denali->flash_reg + INTR_STATUS(i));
-
-       for (i = 0; i < denali->max_banks; i++) {
-               iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
-               while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
-                       (INTR__RST_COMP | INTR__TIME_OUT)))
-                       cpu_relax();
-               if (ioread32(denali->flash_reg + INTR_STATUS(i)) &
-                       INTR__TIME_OUT)
-                       dev_dbg(denali->dev,
-                       "NAND Reset operation timed out on bank %d\n", i);
-       }
-
-       for (i = 0; i < denali->max_banks; i++)
-               iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
-                         denali->flash_reg + INTR_STATUS(i));
-
-       return PASS;
-}
-
-/*
- * this routine calculates the ONFI timing values for a given mode and
- * programs the clocking register accordingly. The mode is determined by
- * the get_onfi_nand_para routine.
- */
-static void nand_onfi_timing_set(struct denali_nand_info *denali,
-                                                               uint16_t mode)
-{
-       uint16_t Trea[6] = {40, 30, 25, 20, 20, 16};
-       uint16_t Trp[6] = {50, 25, 17, 15, 12, 10};
-       uint16_t Treh[6] = {30, 15, 15, 10, 10, 7};
-       uint16_t Trc[6] = {100, 50, 35, 30, 25, 20};
-       uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15};
-       uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5};
-       uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25};
-       uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70};
-       uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100};
-       uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100};
-       uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60};
-       uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15};
-
-       uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid;
-       uint16_t dv_window = 0;
-       uint16_t en_lo, en_hi;
-       uint16_t acc_clks;
-       uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt;
-
-       en_lo = CEIL_DIV(Trp[mode], CLK_X);
-       en_hi = CEIL_DIV(Treh[mode], CLK_X);
-#if ONFI_BLOOM_TIME
-       if ((en_hi * CLK_X) < (Treh[mode] + 2))
-               en_hi++;
-#endif
-
-       if ((en_lo + en_hi) * CLK_X < Trc[mode])
-               en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X);
-
-       if ((en_lo + en_hi) < CLK_MULTI)
-               en_lo += CLK_MULTI - en_lo - en_hi;
-
-       while (dv_window < 8) {
-               data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode];
-
-               data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode];
-
-               data_invalid = data_invalid_rhoh < data_invalid_rloh ?
-                                       data_invalid_rhoh : data_invalid_rloh;
-
-               dv_window = data_invalid - Trea[mode];
-
-               if (dv_window < 8)
-                       en_lo++;
-       }
-
-       acc_clks = CEIL_DIV(Trea[mode], CLK_X);
-
-       while (acc_clks * CLK_X - Trea[mode] < 3)
-               acc_clks++;
-
-       if (data_invalid - acc_clks * CLK_X < 2)
-               dev_warn(denali->dev, "%s, Line %d: Warning!\n",
-                        __FILE__, __LINE__);
-
-       addr_2_data = CEIL_DIV(Tadl[mode], CLK_X);
-       re_2_we = CEIL_DIV(Trhw[mode], CLK_X);
-       re_2_re = CEIL_DIV(Trhz[mode], CLK_X);
-       we_2_re = CEIL_DIV(Twhr[mode], CLK_X);
-       cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X);
-       if (cs_cnt == 0)
-               cs_cnt = 1;
-
-       if (Tcea[mode]) {
-               while (cs_cnt * CLK_X + Trea[mode] < Tcea[mode])
-                       cs_cnt++;
-       }
-
-#if MODE5_WORKAROUND
-       if (mode == 5)
-               acc_clks = 5;
-#endif
-
-       /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */
-       if (ioread32(denali->flash_reg + MANUFACTURER_ID) == 0 &&
-               ioread32(denali->flash_reg + DEVICE_ID) == 0x88)
-               acc_clks = 6;
-
-       iowrite32(acc_clks, denali->flash_reg + ACC_CLKS);
-       iowrite32(re_2_we, denali->flash_reg + RE_2_WE);
-       iowrite32(re_2_re, denali->flash_reg + RE_2_RE);
-       iowrite32(we_2_re, denali->flash_reg + WE_2_RE);
-       iowrite32(addr_2_data, denali->flash_reg + ADDR_2_DATA);
-       iowrite32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT);
-       iowrite32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT);
-       iowrite32(cs_cnt, denali->flash_reg + CS_SETUP_CNT);
-}
-
-/* queries the NAND device to see what ONFI modes it supports. */
-static uint16_t get_onfi_nand_para(struct denali_nand_info *denali)
+static void denali_host_write(struct denali_nand_info *denali,
+                             uint32_t addr, uint32_t data)
 {
-       int i;
-
-       /*
-        * we needn't to do a reset here because driver has already
-        * reset all the banks before
-        */
-       if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
-               ONFI_TIMING_MODE__VALUE))
-               return FAIL;
-
-       for (i = 5; i > 0; i--) {
-               if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
-                       (0x01 << i))
-                       break;
-       }
-
-       nand_onfi_timing_set(denali, i);
-
-       /*
-        * By now, all the ONFI devices we know support the page cache
-        * rw feature. So here we enable the pipeline_rw_ahead feature
-        */
-       /* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */
-       /* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE);  */
-
-       return PASS;
-}
-
-static void get_samsung_nand_para(struct denali_nand_info *denali,
-                                                       uint8_t device_id)
-{
-       if (device_id == 0xd3) { /* Samsung K9WAG08U1A */
-               /* Set timing register values according to datasheet */
-               iowrite32(5, denali->flash_reg + ACC_CLKS);
-               iowrite32(20, denali->flash_reg + RE_2_WE);
-               iowrite32(12, denali->flash_reg + WE_2_RE);
-               iowrite32(14, denali->flash_reg + ADDR_2_DATA);
-               iowrite32(3, denali->flash_reg + RDWR_EN_LO_CNT);
-               iowrite32(2, denali->flash_reg + RDWR_EN_HI_CNT);
-               iowrite32(2, denali->flash_reg + CS_SETUP_CNT);
-       }
-}
-
-static void get_toshiba_nand_para(struct denali_nand_info *denali)
-{
-       /*
-        * Workaround to fix a controller bug which reports a wrong
-        * spare area size for some kind of Toshiba NAND device
-        */
-       if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) &&
-               (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64))
-               iowrite32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-}
-
-static void get_hynix_nand_para(struct denali_nand_info *denali,
-                                                       uint8_t device_id)
-{
-       switch (device_id) {
-       case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */
-       case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */
-               iowrite32(128, denali->flash_reg + PAGES_PER_BLOCK);
-               iowrite32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
-               iowrite32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-               iowrite32(0, denali->flash_reg + DEVICE_WIDTH);
-               break;
-       default:
-               dev_warn(denali->dev,
-                        "Unknown Hynix NAND (Device ID: 0x%x).\n"
-                        "Will use default parameter values instead.\n",
-                        device_id);
-       }
-}
-
-/*
- * determines how many NAND chips are connected to the controller. Note for
- * Intel CE4100 devices we don't support more than one device.
- */
-static void find_valid_banks(struct denali_nand_info *denali)
-{
-       uint32_t id[denali->max_banks];
-       int i;
-
-       denali->total_used_banks = 1;
-       for (i = 0; i < denali->max_banks; i++) {
-               index_addr(denali, MODE_11 | (i << 24) | 0, 0x90);
-               index_addr(denali, MODE_11 | (i << 24) | 1, 0);
-               index_addr_read_data(denali, MODE_11 | (i << 24) | 2, &id[i]);
-
-               dev_dbg(denali->dev,
-                       "Return 1st ID for bank[%d]: %x\n", i, id[i]);
-
-               if (i == 0) {
-                       if (!(id[i] & 0x0ff))
-                               break; /* WTF? */
-               } else {
-                       if ((id[i] & 0x0ff) == (id[0] & 0x0ff))
-                               denali->total_used_banks++;
-                       else
-                               break;
-               }
-       }
-
-       if (denali->platform == INTEL_CE4100) {
-               /*
-                * Platform limitations of the CE4100 device limit
-                * users to a single chip solution for NAND.
-                * Multichip support is not enabled.
-                */
-               if (denali->total_used_banks != 1) {
-                       dev_err(denali->dev,
-                               "Sorry, Intel CE4100 only supports a single NAND device.\n");
-                       BUG();
-               }
-       }
-       dev_dbg(denali->dev,
-               "denali->total_used_banks: %d\n", denali->total_used_banks);
+       iowrite32(addr, denali->host + DENALI_HOST_ADDR);
+       iowrite32(data, denali->host + DENALI_HOST_DATA);
 }
 
 /*
@@ -418,7 +83,7 @@ static void find_valid_banks(struct denali_nand_info *denali)
  */
 static void detect_max_banks(struct denali_nand_info *denali)
 {
-       uint32_t features = ioread32(denali->flash_reg + FEATURES);
+       uint32_t features = ioread32(denali->reg + FEATURES);
 
        denali->max_banks = 1 << (features & FEATURES__N_BANKS);
 
@@ -427,227 +92,120 @@ static void detect_max_banks(struct denali_nand_info *denali)
                denali->max_banks <<= 1;
 }
 
-static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
+static void denali_enable_irq(struct denali_nand_info *denali)
 {
-       uint16_t status = PASS;
-       uint32_t id_bytes[8], addr;
-       uint8_t maf_id, device_id;
        int i;
 
-       /*
-        * Use read id method to get device ID and other params.
-        * For some NAND chips, controller can't report the correct
-        * device ID by reading from DEVICE_ID register
-        */
-       addr = MODE_11 | BANK(denali->flash_bank);
-       index_addr(denali, addr | 0, 0x90);
-       index_addr(denali, addr | 1, 0);
-       for (i = 0; i < 8; i++)
-               index_addr_read_data(denali, addr | 2, &id_bytes[i]);
-       maf_id = id_bytes[0];
-       device_id = id_bytes[1];
-
-       if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) &
-               ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */
-               if (FAIL == get_onfi_nand_para(denali))
-                       return FAIL;
-       } else if (maf_id == 0xEC) { /* Samsung NAND */
-               get_samsung_nand_para(denali, device_id);
-       } else if (maf_id == 0x98) { /* Toshiba NAND */
-               get_toshiba_nand_para(denali);
-       } else if (maf_id == 0xAD) { /* Hynix NAND */
-               get_hynix_nand_para(denali, device_id);
-       }
-
-       dev_info(denali->dev,
-                       "Dump timing register values:\n"
-                       "acc_clks: %d, re_2_we: %d, re_2_re: %d\n"
-                       "we_2_re: %d, addr_2_data: %d, rdwr_en_lo_cnt: %d\n"
-                       "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n",
-                       ioread32(denali->flash_reg + ACC_CLKS),
-                       ioread32(denali->flash_reg + RE_2_WE),
-                       ioread32(denali->flash_reg + RE_2_RE),
-                       ioread32(denali->flash_reg + WE_2_RE),
-                       ioread32(denali->flash_reg + ADDR_2_DATA),
-                       ioread32(denali->flash_reg + RDWR_EN_LO_CNT),
-                       ioread32(denali->flash_reg + RDWR_EN_HI_CNT),
-                       ioread32(denali->flash_reg + CS_SETUP_CNT));
-
-       find_valid_banks(denali);
-
-       /*
-        * If the user specified to override the default timings
-        * with a specific ONFI mode, we apply those changes here.
-        */
-       if (onfi_timing_mode != NAND_DEFAULT_TIMINGS)
-               nand_onfi_timing_set(denali, onfi_timing_mode);
-
-       return status;
+       for (i = 0; i < DENALI_NR_BANKS; i++)
+               iowrite32(U32_MAX, denali->reg + INTR_EN(i));
+       iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
 }
 
-static void denali_set_intr_modes(struct denali_nand_info *denali,
-                                       uint16_t INT_ENABLE)
+static void denali_disable_irq(struct denali_nand_info *denali)
 {
-       if (INT_ENABLE)
-               iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
-       else
-               iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
-}
-
-/*
- * validation function to verify that the controlling software is making
- * a valid request
- */
-static inline bool is_flash_bank_valid(int flash_bank)
-{
-       return flash_bank >= 0 && flash_bank < 4;
-}
-
-static void denali_irq_init(struct denali_nand_info *denali)
-{
-       uint32_t int_mask;
        int i;
 
-       /* Disable global interrupts */
-       denali_set_intr_modes(denali, false);
-
-       int_mask = DENALI_IRQ_ALL;
-
-       /* Clear all status bits */
-       for (i = 0; i < denali->max_banks; ++i)
-               iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS(i));
-
-       denali_irq_enable(denali, int_mask);
+       for (i = 0; i < DENALI_NR_BANKS; i++)
+               iowrite32(0, denali->reg + INTR_EN(i));
+       iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
 }
 
-static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali)
+static void denali_clear_irq(struct denali_nand_info *denali,
+                            int bank, uint32_t irq_status)
 {
-       denali_set_intr_modes(denali, false);
+       /* write one to clear bits */
+       iowrite32(irq_status, denali->reg + INTR_STATUS(bank));
 }
 
-static void denali_irq_enable(struct denali_nand_info *denali,
-                                                       uint32_t int_mask)
+static void denali_clear_irq_all(struct denali_nand_info *denali)
 {
        int i;
 
-       for (i = 0; i < denali->max_banks; ++i)
-               iowrite32(int_mask, denali->flash_reg + INTR_EN(i));
+       for (i = 0; i < DENALI_NR_BANKS; i++)
+               denali_clear_irq(denali, i, U32_MAX);
 }
 
-/*
- * This function only returns when an interrupt that this driver cares about
- * occurs. This is to reduce the overhead of servicing interrupts
- */
-static inline uint32_t denali_irq_detected(struct denali_nand_info *denali)
+static irqreturn_t denali_isr(int irq, void *dev_id)
 {
-       return read_interrupt_status(denali) & DENALI_IRQ_ALL;
-}
+       struct denali_nand_info *denali = dev_id;
+       irqreturn_t ret = IRQ_NONE;
+       uint32_t irq_status;
+       int i;
 
-/* Interrupts are cleared by writing a 1 to the appropriate status bit */
-static inline void clear_interrupt(struct denali_nand_info *denali,
-                                                       uint32_t irq_mask)
-{
-       uint32_t intr_status_reg;
+       spin_lock(&denali->irq_lock);
 
-       intr_status_reg = INTR_STATUS(denali->flash_bank);
+       for (i = 0; i < DENALI_NR_BANKS; i++) {
+               irq_status = ioread32(denali->reg + INTR_STATUS(i));
+               if (irq_status)
+                       ret = IRQ_HANDLED;
 
-       iowrite32(irq_mask, denali->flash_reg + intr_status_reg);
-}
+               denali_clear_irq(denali, i, irq_status);
 
-static void clear_interrupts(struct denali_nand_info *denali)
-{
-       uint32_t status;
+               if (i != denali->active_bank)
+                       continue;
 
-       spin_lock_irq(&denali->irq_lock);
+               denali->irq_status |= irq_status;
 
-       status = read_interrupt_status(denali);
-       clear_interrupt(denali, status);
+               if (denali->irq_status & denali->irq_mask)
+                       complete(&denali->complete);
+       }
+
+       spin_unlock(&denali->irq_lock);
 
-       denali->irq_status = 0x0;
-       spin_unlock_irq(&denali->irq_lock);
+       return ret;
 }
 
-static uint32_t read_interrupt_status(struct denali_nand_info *denali)
+static void denali_reset_irq(struct denali_nand_info *denali)
 {
-       uint32_t intr_status_reg;
-
-       intr_status_reg = INTR_STATUS(denali->flash_bank);
+       unsigned long flags;
 
-       return ioread32(denali->flash_reg + intr_status_reg);
+       spin_lock_irqsave(&denali->irq_lock, flags);
+       denali->irq_status = 0;
+       denali->irq_mask = 0;
+       spin_unlock_irqrestore(&denali->irq_lock, flags);
 }
 
-/*
- * This is the interrupt service routine. It handles all interrupts
- * sent to this device. Note that on CE4100, this is a shared interrupt.
- */
-static irqreturn_t denali_isr(int irq, void *dev_id)
+static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
+                                   uint32_t irq_mask)
 {
-       struct denali_nand_info *denali = dev_id;
+       unsigned long time_left, flags;
        uint32_t irq_status;
-       irqreturn_t result = IRQ_NONE;
 
-       spin_lock(&denali->irq_lock);
+       spin_lock_irqsave(&denali->irq_lock, flags);
 
-       /* check to see if a valid NAND chip has been selected. */
-       if (is_flash_bank_valid(denali->flash_bank)) {
-               /*
-                * check to see if controller generated the interrupt,
-                * since this is a shared interrupt
-                */
-               irq_status = denali_irq_detected(denali);
-               if (irq_status != 0) {
-                       /* handle interrupt */
-                       /* first acknowledge it */
-                       clear_interrupt(denali, irq_status);
-                       /*
-                        * store the status in the device context for someone
-                        * to read
-                        */
-                       denali->irq_status |= irq_status;
-                       /* notify anyone who cares that it happened */
-                       complete(&denali->complete);
-                       /* tell the OS that we've handled this */
-                       result = IRQ_HANDLED;
-               }
+       irq_status = denali->irq_status;
+
+       if (irq_mask & irq_status) {
+               /* return immediately if the IRQ has already happened. */
+               spin_unlock_irqrestore(&denali->irq_lock, flags);
+               return irq_status;
        }
-       spin_unlock(&denali->irq_lock);
-       return result;
-}
 
-static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
-{
-       unsigned long comp_res;
-       uint32_t intr_status;
-       unsigned long timeout = msecs_to_jiffies(1000);
+       denali->irq_mask = irq_mask;
+       reinit_completion(&denali->complete);
+       spin_unlock_irqrestore(&denali->irq_lock, flags);
 
-       do {
-               comp_res =
-                       wait_for_completion_timeout(&denali->complete, timeout);
-               spin_lock_irq(&denali->irq_lock);
-               intr_status = denali->irq_status;
-
-               if (intr_status & irq_mask) {
-                       denali->irq_status &= ~irq_mask;
-                       spin_unlock_irq(&denali->irq_lock);
-                       /* our interrupt was detected */
-                       break;
-               }
+       time_left = wait_for_completion_timeout(&denali->complete,
+                                               msecs_to_jiffies(1000));
+       if (!time_left) {
+               dev_err(denali->dev, "timeout while waiting for irq 0x%x\n",
+                       denali->irq_mask);
+               return 0;
+       }
 
-               /*
-                * these are not the interrupts you are looking for -
-                * need to wait again
-                */
-               spin_unlock_irq(&denali->irq_lock);
-       } while (comp_res != 0);
+       return denali->irq_status;
+}
+
+static uint32_t denali_check_irq(struct denali_nand_info *denali)
+{
+       unsigned long flags;
+       uint32_t irq_status;
 
-       if (comp_res == 0) {
-               /* timeout */
-               pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n",
-                               intr_status, irq_mask);
+       spin_lock_irqsave(&denali->irq_lock, flags);
+       irq_status = denali->irq_status;
+       spin_unlock_irqrestore(&denali->irq_lock, flags);
 
-               intr_status = 0;
-       }
-       return intr_status;
+       return irq_status;
 }
 
 /*
@@ -664,153 +222,111 @@ static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en,
        transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0;
 
        /* Enable spare area/ECC per user's request. */
-       iowrite32(ecc_en_flag, denali->flash_reg + ECC_ENABLE);
-       iowrite32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG);
+       iowrite32(ecc_en_flag, denali->reg + ECC_ENABLE);
+       iowrite32(transfer_spare_flag, denali->reg + TRANSFER_SPARE_REG);
 }
 
-/*
- * sends a pipeline command operation to the controller. See the Denali NAND
- * controller's user guide for more information (section 4.2.3.6).
- */
-static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
-                                   bool ecc_en, bool transfer_spare,
-                                   int access_type, int op)
+static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-       int status = PASS;
-       uint32_t addr, cmd;
-
-       setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
+       struct denali_nand_info *denali = mtd_to_denali(mtd);
+       int i;
 
-       clear_interrupts(denali);
+       iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
+                 denali->host + DENALI_HOST_ADDR);
 
-       addr = BANK(denali->flash_bank) | denali->page;
+       for (i = 0; i < len; i++)
+               buf[i] = ioread32(denali->host + DENALI_HOST_DATA);
+}
 
-       if (op == DENALI_WRITE && access_type != SPARE_ACCESS) {
-               cmd = MODE_01 | addr;
-               iowrite32(cmd, denali->flash_mem);
-       } else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) {
-               /* read spare area */
-               cmd = MODE_10 | addr;
-               index_addr(denali, cmd, access_type);
+static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+       struct denali_nand_info *denali = mtd_to_denali(mtd);
+       int i;
 
-               cmd = MODE_01 | addr;
-               iowrite32(cmd, denali->flash_mem);
-       } else if (op == DENALI_READ) {
-               /* setup page read request for access type */
-               cmd = MODE_10 | addr;
-               index_addr(denali, cmd, access_type);
+       iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
+                 denali->host + DENALI_HOST_ADDR);
 
-               cmd = MODE_01 | addr;
-               iowrite32(cmd, denali->flash_mem);
-       }
-       return status;
+       for (i = 0; i < len; i++)
+               iowrite32(buf[i], denali->host + DENALI_HOST_DATA);
 }
 
-/* helper function that simply writes a buffer to the flash */
-static int write_data_to_flash_mem(struct denali_nand_info *denali,
-                                  const uint8_t *buf, int len)
+static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
 {
-       uint32_t *buf32;
+       struct denali_nand_info *denali = mtd_to_denali(mtd);
+       uint16_t *buf16 = (uint16_t *)buf;
        int i;
 
-       /*
-        * verify that the len is a multiple of 4.
-        * see comment in read_data_from_flash_mem()
-        */
-       BUG_ON((len % 4) != 0);
+       iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
+                 denali->host + DENALI_HOST_ADDR);
 
-       /* write the data to the flash memory */
-       buf32 = (uint32_t *)buf;
-       for (i = 0; i < len / 4; i++)
-               iowrite32(*buf32++, denali->flash_mem + 0x10);
-       return i * 4; /* intent is to return the number of bytes read */
+       for (i = 0; i < len / 2; i++)
+               buf16[i] = ioread32(denali->host + DENALI_HOST_DATA);
 }
 
-/* helper function that simply reads a buffer from the flash */
-static int read_data_from_flash_mem(struct denali_nand_info *denali,
-                                   uint8_t *buf, int len)
+static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf,
+                              int len)
 {
-       uint32_t *buf32;
+       struct denali_nand_info *denali = mtd_to_denali(mtd);
+       const uint16_t *buf16 = (const uint16_t *)buf;
        int i;
 
-       /*
-        * we assume that len will be a multiple of 4, if not it would be nice
-        * to know about it ASAP rather than have random failures...
-        * This assumption is based on the fact that this function is designed
-        * to be used to read flash pages, which are typically multiples of 4.
-        */
-       BUG_ON((len % 4) != 0);
+       iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
+                 denali->host + DENALI_HOST_ADDR);
 
-       /* transfer the data from the flash */
-       buf32 = (uint32_t *)buf;
-       for (i = 0; i < len / 4; i++)
-               *buf32++ = ioread32(denali->flash_mem + 0x10);
-       return i * 4; /* intent is to return the number of bytes read */
+       for (i = 0; i < len / 2; i++)
+               iowrite32(buf16[i], denali->host + DENALI_HOST_DATA);
 }
 
-/* writes OOB data to the device */
-static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
+static uint8_t denali_read_byte(struct mtd_info *mtd)
 {
-       struct denali_nand_info *denali = mtd_to_denali(mtd);
-       uint32_t irq_status;
-       uint32_t irq_mask = INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL;
-       int status = 0;
+       uint8_t byte;
 
-       denali->page = page;
+       denali_read_buf(mtd, &byte, 1);
 
-       if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS,
-                                                       DENALI_WRITE) == PASS) {
-               write_data_to_flash_mem(denali, buf, mtd->oobsize);
+       return byte;
+}
 
-               /* wait for operation to complete */
-               irq_status = wait_for_irq(denali, irq_mask);
+static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+       denali_write_buf(mtd, &byte, 1);
+}
 
-               if (irq_status == 0) {
-                       dev_err(denali->dev, "OOB write failed\n");
-                       status = -EIO;
-               }
-       } else {
-               dev_err(denali->dev, "unable to send pipeline command\n");
-               status = -EIO;
-       }
-       return status;
+static uint16_t denali_read_word(struct mtd_info *mtd)
+{
+       uint16_t word;
+
+       denali_read_buf16(mtd, (uint8_t *)&word, 2);
+
+       return word;
 }
 
-/* reads OOB data from the device */
-static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
+static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
 {
        struct denali_nand_info *denali = mtd_to_denali(mtd);
-       uint32_t irq_mask = INTR__LOAD_COMP;
-       uint32_t irq_status, addr, cmd;
+       uint32_t type;
 
-       denali->page = page;
+       if (ctrl & NAND_CLE)
+               type = DENALI_MAP11_CMD;
+       else if (ctrl & NAND_ALE)
+               type = DENALI_MAP11_ADDR;
+       else
+               return;
 
-       if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS,
-                                                       DENALI_READ) == PASS) {
-               read_data_from_flash_mem(denali, buf, mtd->oobsize);
+       /*
+        * Some commands are followed by chip->dev_ready or chip->waitfunc.
+        * irq_status must be cleared here to catch the R/B# interrupt later.
+        */
+       if (ctrl & NAND_CTRL_CHANGE)
+               denali_reset_irq(denali);
 
-               /*
-                * wait for command to be accepted
-                * can always use status0 bit as the
-                * mask is identical for each bank.
-                */
-               irq_status = wait_for_irq(denali, irq_mask);
+       denali_host_write(denali, DENALI_BANK(denali) | type, dat);
+}
 
-               if (irq_status == 0)
-                       dev_err(denali->dev, "page on OOB timeout %d\n",
-                                       denali->page);
+static int denali_dev_ready(struct mtd_info *mtd)
+{
+       struct denali_nand_info *denali = mtd_to_denali(mtd);
 
-               /*
-                * We set the device back to MAIN_ACCESS here as I observed
-                * instability with the controller if you do a block erase
-                * and the last transaction was a SPARE_ACCESS. Block erase
-                * is reliable (according to the MTD test infrastructure)
-                * if you are in MAIN_ACCESS.
-                */
-               addr = BANK(denali->flash_bank) | denali->page;
-               cmd = MODE_10 | addr;
-               index_addr(denali, cmd, MAIN_ACCESS);
-       }
+       return !!(denali_check_irq(denali) & INTR__INT_ACT);
 }
 
 static int denali_check_erased_page(struct mtd_info *mtd,
@@ -856,11 +372,11 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
                               unsigned long *uncor_ecc_flags)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
-       int bank = denali->flash_bank;
+       int bank = denali->active_bank;
        uint32_t ecc_cor;
        unsigned int max_bitflips;
 
-       ecc_cor = ioread32(denali->flash_reg + ECC_COR_INFO(bank));
+       ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank));
        ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
 
        if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
@@ -886,8 +402,6 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
        return max_bitflips;
 }
 
-#define ECC_SECTOR_SIZE 512
-
 #define ECC_SECTOR(x)  (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
 #define ECC_BYTE(x)    (((x) & ECC_ERROR_ADDRESS__OFFSET))
 #define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
@@ -899,22 +413,23 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
                               struct denali_nand_info *denali,
                               unsigned long *uncor_ecc_flags, uint8_t *buf)
 {
+       unsigned int ecc_size = denali->nand.ecc.size;
        unsigned int bitflips = 0;
        unsigned int max_bitflips = 0;
        uint32_t err_addr, err_cor_info;
        unsigned int err_byte, err_sector, err_device;
        uint8_t err_cor_value;
        unsigned int prev_sector = 0;
+       uint32_t irq_status;
 
-       /* read the ECC errors. we'll ignore them for now */
-       denali_set_intr_modes(denali, false);
+       denali_reset_irq(denali);
 
        do {
-               err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
+               err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS);
                err_sector = ECC_SECTOR(err_addr);
                err_byte = ECC_BYTE(err_addr);
 
-               err_cor_info = ioread32(denali->flash_reg + ERR_CORRECTION_INFO);
+               err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO);
                err_cor_value = ECC_CORRECTION_VALUE(err_cor_info);
                err_device = ECC_ERR_DEVICE(err_cor_info);
 
@@ -928,9 +443,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
                         * an erased sector.
                         */
                        *uncor_ecc_flags |= BIT(err_sector);
-               } else if (err_byte < ECC_SECTOR_SIZE) {
+               } else if (err_byte < ecc_size) {
                        /*
-                        * If err_byte is larger than ECC_SECTOR_SIZE, means error
+                        * If err_byte is larger than ecc_size, means error
                         * happened in OOB, so we ignore it. It's no need for
                         * us to correct it err_device is represented the NAND
                         * error bits are happened in if there are more than
@@ -939,8 +454,8 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
                        int offset;
                        unsigned int flips_in_byte;
 
-                       offset = (err_sector * ECC_SECTOR_SIZE + err_byte) *
-                                               denali->devnum + err_device;
+                       offset = (err_sector * ecc_size + err_byte) *
+                                       denali->devs_per_cs + err_device;
 
                        /* correct the ECC error */
                        flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
@@ -959,10 +474,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
         * ECC_TRANSACTION_DONE interrupt, so here just wait for
         * a while for this interrupt
         */
-       while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE))
-               cpu_relax();
-       clear_interrupts(denali);
-       denali_set_intr_modes(denali, true);
+       irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
+       if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
+               return -EIO;
 
        return max_bitflips;
 }
@@ -970,17 +484,17 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
 /* programs the controller to either enable/disable DMA transfers */
 static void denali_enable_dma(struct denali_nand_info *denali, bool en)
 {
-       iowrite32(en ? DMA_ENABLE__FLAG : 0, denali->flash_reg + DMA_ENABLE);
-       ioread32(denali->flash_reg + DMA_ENABLE);
+       iowrite32(en ? DMA_ENABLE__FLAG : 0, denali->reg + DMA_ENABLE);
+       ioread32(denali->reg + DMA_ENABLE);
 }
 
-static void denali_setup_dma64(struct denali_nand_info *denali, int op)
+static void denali_setup_dma64(struct denali_nand_info *denali,
+                              dma_addr_t dma_addr, int page, int write)
 {
        uint32_t mode;
        const int page_count = 1;
-       uint64_t addr = denali->buf.dma_buf;
 
-       mode = MODE_10 | BANK(denali->flash_bank) | denali->page;
+       mode = DENALI_MAP10 | DENALI_BANK(denali) | page;
 
        /* DMA is a three step process */
 
@@ -988,191 +502,354 @@ static void denali_setup_dma64(struct denali_nand_info *denali, int op)
         * 1. setup transfer type, interrupt when complete,
         *    burst len = 64 bytes, the number of pages
         */
-       index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
+       denali_host_write(denali, mode,
+                         0x01002000 | (64 << 16) | (write << 8) | page_count);
 
        /* 2. set memory low address */
-       index_addr(denali, mode, addr);
+       denali_host_write(denali, mode, dma_addr);
 
        /* 3. set memory high address */
-       index_addr(denali, mode, addr >> 32);
+       denali_host_write(denali, mode, (uint64_t)dma_addr >> 32);
 }
 
-static void denali_setup_dma32(struct denali_nand_info *denali, int op)
+static void denali_setup_dma32(struct denali_nand_info *denali,
+                              dma_addr_t dma_addr, int page, int write)
 {
        uint32_t mode;
        const int page_count = 1;
-       uint32_t addr = denali->buf.dma_buf;
 
-       mode = MODE_10 | BANK(denali->flash_bank);
+       mode = DENALI_MAP10 | DENALI_BANK(denali);
 
        /* DMA is a four step process */
 
        /* 1. setup transfer type and # of pages */
-       index_addr(denali, mode | denali->page, 0x2000 | op | page_count);
+       denali_host_write(denali, mode | page,
+                         0x2000 | (write << 8) | page_count);
 
        /* 2. set memory high address bits 23:8 */
-       index_addr(denali, mode | ((addr >> 16) << 8), 0x2200);
+       denali_host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
 
        /* 3. set memory low address bits 23:8 */
-       index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300);
+       denali_host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300);
 
        /* 4. interrupt when complete, burst len = 64 bytes */
-       index_addr(denali, mode | 0x14000, 0x2400);
+       denali_host_write(denali, mode | 0x14000, 0x2400);
 }
 
-static void denali_setup_dma(struct denali_nand_info *denali, int op)
+static void denali_setup_dma(struct denali_nand_info *denali,
+                            dma_addr_t dma_addr, int page, int write)
 {
        if (denali->caps & DENALI_CAP_DMA_64BIT)
-               denali_setup_dma64(denali, op);
+               denali_setup_dma64(denali, dma_addr, page, write);
        else
-               denali_setup_dma32(denali, op);
+               denali_setup_dma32(denali, dma_addr, page, write);
 }
 
-/*
- * writes a page. user specifies type, and this function handles the
- * configuration details.
- */
-static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
-                       const uint8_t *buf, bool raw_xfer)
+static int denali_pio_read(struct denali_nand_info *denali, void *buf,
+                          size_t size, int page, int raw)
 {
-       struct denali_nand_info *denali = mtd_to_denali(mtd);
-       dma_addr_t addr = denali->buf.dma_buf;
-       size_t size = mtd->writesize + mtd->oobsize;
+       uint32_t addr = DENALI_BANK(denali) | page;
+       uint32_t *buf32 = (uint32_t *)buf;
+       uint32_t irq_status, ecc_err_mask;
+       int i;
+
+       if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+               ecc_err_mask = INTR__ECC_UNCOR_ERR;
+       else
+               ecc_err_mask = INTR__ECC_ERR;
+
+       denali_reset_irq(denali);
+
+       iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR);
+       for (i = 0; i < size / 4; i++)
+               *buf32++ = ioread32(denali->host + DENALI_HOST_DATA);
+
+       irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
+       if (!(irq_status & INTR__PAGE_XFER_INC))
+               return -EIO;
+
+       if (irq_status & INTR__ERASED_PAGE)
+               memset(buf, 0xff, size);
+
+       return irq_status & ecc_err_mask ? -EBADMSG : 0;
+}
+
+static int denali_pio_write(struct denali_nand_info *denali,
+                           const void *buf, size_t size, int page, int raw)
+{
+       uint32_t addr = DENALI_BANK(denali) | page;
+       const uint32_t *buf32 = (uint32_t *)buf;
        uint32_t irq_status;
-       uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+       int i;
 
-       /*
-        * if it is a raw xfer, we want to disable ecc and send the spare area.
-        * !raw_xfer - enable ecc
-        * raw_xfer - transfer spare
-        */
-       setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer);
+       denali_reset_irq(denali);
 
-       /* copy buffer into DMA buffer */
-       memcpy(denali->buf.buf, buf, mtd->writesize);
+       iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR);
+       for (i = 0; i < size / 4; i++)
+               iowrite32(*buf32++, denali->host + DENALI_HOST_DATA);
 
-       if (raw_xfer) {
-               /* transfer the data to the spare area */
-               memcpy(denali->buf.buf + mtd->writesize,
-                       chip->oob_poi,
-                       mtd->oobsize);
+       irq_status = denali_wait_for_irq(denali,
+                               INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
+       if (!(irq_status & INTR__PROGRAM_COMP))
+               return -EIO;
+
+       return 0;
+}
+
+static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
+                          size_t size, int page, int raw, int write)
+{
+       if (write)
+               return denali_pio_write(denali, buf, size, page, raw);
+       else
+               return denali_pio_read(denali, buf, size, page, raw);
+}
+
+static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
+                          size_t size, int page, int raw, int write)
+{
+       dma_addr_t dma_addr;
+       uint32_t irq_mask, irq_status, ecc_err_mask;
+       enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+       int ret = 0;
+
+       dma_addr = dma_map_single(denali->dev, buf, size, dir);
+       if (dma_mapping_error(denali->dev, dma_addr)) {
+               dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n");
+               return denali_pio_xfer(denali, buf, size, page, raw, write);
        }
 
-       dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE);
+       if (write) {
+               /*
+                * INTR__PROGRAM_COMP is never asserted for the DMA transfer.
+                * We can use INTR__DMA_CMD_COMP instead.  This flag is asserted
+                * when the page program is completed.
+                */
+               irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+               ecc_err_mask = 0;
+       } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) {
+               irq_mask = INTR__DMA_CMD_COMP;
+               ecc_err_mask = INTR__ECC_UNCOR_ERR;
+       } else {
+               irq_mask = INTR__DMA_CMD_COMP;
+               ecc_err_mask = INTR__ECC_ERR;
+       }
 
-       clear_interrupts(denali);
        denali_enable_dma(denali, true);
 
-       denali_setup_dma(denali, DENALI_WRITE);
+       denali_reset_irq(denali);
+       denali_setup_dma(denali, dma_addr, page, write);
 
        /* wait for operation to complete */
-       irq_status = wait_for_irq(denali, irq_mask);
-
-       if (irq_status == 0) {
-               dev_err(denali->dev, "timeout on write_page (type = %d)\n",
-                       raw_xfer);
-               denali->status = NAND_STATUS_FAIL;
-       }
+       irq_status = denali_wait_for_irq(denali, irq_mask);
+       if (!(irq_status & INTR__DMA_CMD_COMP))
+               ret = -EIO;
+       else if (irq_status & ecc_err_mask)
+               ret = -EBADMSG;
 
        denali_enable_dma(denali, false);
-       dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
+       dma_unmap_single(denali->dev, dma_addr, size, dir);
 
-       return 0;
-}
+       if (irq_status & INTR__ERASED_PAGE)
+               memset(buf, 0xff, size);
 
-/* NAND core entry points */
+       return ret;
+}
 
-/*
- * this is the callback that the NAND core calls to write a page. Since
- * writing a page with ECC or without is similar, all the work is done
- * by write_page above.
- */
-static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
-                               const uint8_t *buf, int oob_required, int page)
+static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
+                           size_t size, int page, int raw, int write)
 {
-       /*
-        * for regular page writes, we let HW handle all the ECC
-        * data written to the device.
-        */
-       return write_page(mtd, chip, buf, false);
+       setup_ecc_for_xfer(denali, !raw, raw);
+
+       if (denali->dma_avail)
+               return denali_dma_xfer(denali, buf, size, page, raw, write);
+       else
+               return denali_pio_xfer(denali, buf, size, page, raw, write);
 }
 
-/*
- * This is the callback that the NAND core calls to write a page without ECC.
- * raw access is similar to ECC page writes, so all the work is done in the
- * write_page() function above.
- */
-static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-                                const uint8_t *buf, int oob_required,
-                                int page)
+static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
+                           int page, int write)
 {
-       /*
-        * for raw page writes, we want to disable ECC and simply write
-        * whatever data is in the buffer.
-        */
-       return write_page(mtd, chip, buf, true);
+       struct denali_nand_info *denali = mtd_to_denali(mtd);
+       unsigned int start_cmd = write ? NAND_CMD_SEQIN : NAND_CMD_READ0;
+       unsigned int rnd_cmd = write ? NAND_CMD_RNDIN : NAND_CMD_RNDOUT;
+       int writesize = mtd->writesize;
+       int oobsize = mtd->oobsize;
+       uint8_t *bufpoi = chip->oob_poi;
+       int ecc_steps = chip->ecc.steps;
+       int ecc_size = chip->ecc.size;
+       int ecc_bytes = chip->ecc.bytes;
+       int oob_skip = denali->oob_skip_bytes;
+       size_t size = writesize + oobsize;
+       int i, pos, len;
+
+       /* BBM at the beginning of the OOB area */
+       chip->cmdfunc(mtd, start_cmd, writesize, page);
+       if (write)
+               chip->write_buf(mtd, bufpoi, oob_skip);
+       else
+               chip->read_buf(mtd, bufpoi, oob_skip);
+       bufpoi += oob_skip;
+
+       /* OOB ECC */
+       for (i = 0; i < ecc_steps; i++) {
+               pos = ecc_size + i * (ecc_size + ecc_bytes);
+               len = ecc_bytes;
+
+               if (pos >= writesize)
+                       pos += oob_skip;
+               else if (pos + len > writesize)
+                       len = writesize - pos;
+
+               chip->cmdfunc(mtd, rnd_cmd, pos, -1);
+               if (write)
+                       chip->write_buf(mtd, bufpoi, len);
+               else
+                       chip->read_buf(mtd, bufpoi, len);
+               bufpoi += len;
+               if (len < ecc_bytes) {
+                       len = ecc_bytes - len;
+                       chip->cmdfunc(mtd, rnd_cmd, writesize + oob_skip, -1);
+                       if (write)
+                               chip->write_buf(mtd, bufpoi, len);
+                       else
+                               chip->read_buf(mtd, bufpoi, len);
+                       bufpoi += len;
+               }
+       }
+
+       /* OOB free */
+       len = oobsize - (bufpoi - chip->oob_poi);
+       chip->cmdfunc(mtd, rnd_cmd, size - len, -1);
+       if (write)
+               chip->write_buf(mtd, bufpoi, len);
+       else
+               chip->read_buf(mtd, bufpoi, len);
 }
 
-static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
-                           int page)
+static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                               uint8_t *buf, int oob_required, int page)
 {
-       return write_oob_data(mtd, chip->oob_poi, page);
+       struct denali_nand_info *denali = mtd_to_denali(mtd);
+       int writesize = mtd->writesize;
+       int oobsize = mtd->oobsize;
+       int ecc_steps = chip->ecc.steps;
+       int ecc_size = chip->ecc.size;
+       int ecc_bytes = chip->ecc.bytes;
+       void *dma_buf = denali->buf;
+       int oob_skip = denali->oob_skip_bytes;
+       size_t size = writesize + oobsize;
+       int ret, i, pos, len;
+
+       ret = denali_data_xfer(denali, dma_buf, size, page, 1, 0);
+       if (ret)
+               return ret;
+
+       /* Arrange the buffer for syndrome payload/ecc layout */
+       if (buf) {
+               for (i = 0; i < ecc_steps; i++) {
+                       pos = i * (ecc_size + ecc_bytes);
+                       len = ecc_size;
+
+                       if (pos >= writesize)
+                               pos += oob_skip;
+                       else if (pos + len > writesize)
+                               len = writesize - pos;
+
+                       memcpy(buf, dma_buf + pos, len);
+                       buf += len;
+                       if (len < ecc_size) {
+                               len = ecc_size - len;
+                               memcpy(buf, dma_buf + writesize + oob_skip,
+                     &nb